Skip to content

Commit b8468be

Browse files
authored
Merge pull request #409 from fabric-testbed/404-allocation-control-resource-quotas-permissions
404 allocation control resource quotas permissions
2 parents 886a91f + 375bcb4 commit b8468be

30 files changed

+355
-48
lines changed

fabric_cf/actor/boot/configuration.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ def __init__(self, *, config: dict):
4545
if Constants.CONFIG_SECTION_O_AUTH in config:
4646
self.oauth = config.get(Constants.CONFIG_SECTION_O_AUTH)
4747

48+
self.core_api = {}
49+
if Constants.CONFIG_SECTION_CORE_API in config:
50+
self.core_api = config.get(Constants.CONFIG_SECTION_CORE_API)
51+
4852
self.smtp = {}
4953
if Constants.CONFIG_SECTION_SMTP in config:
5054
self.smtp = config.get(Constants.CONFIG_SECTION_SMTP)
@@ -91,6 +95,12 @@ def get_oauth(self) -> dict:
9195
"""
9296
return self.oauth
9397

98+
def get_core_api(self) -> dict:
99+
"""
100+
Return core api
101+
"""
102+
return self.core_api
103+
94104
def get_smtp(self) -> dict:
95105
"""
96106
Return smtp config
@@ -425,15 +435,20 @@ def get_runtime_config(self) -> dict:
425435
"""
426436
if self.global_config is not None:
427437
return self.global_config.get_runtime()
428-
return None
429438

430439
def get_oauth_config(self) -> dict:
431440
"""
432441
Return OAuth Config
433442
"""
434443
if self.global_config is not None:
435444
return self.global_config.get_oauth()
436-
return None
445+
446+
def get_core_api_config(self) -> dict:
447+
"""
448+
Return Core API Config
449+
"""
450+
if self.global_config is not None:
451+
return self.global_config.get_core_api()
437452

438453
def get_smtp_config(self) -> dict:
439454
if self.global_config:

fabric_cf/actor/core/apis/abc_database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,4 @@ def get_poas(self, *, poa_id: str = None, email: str = None, sliver_id: ID = Non
477477
@param offset offset
478478
@param states states
479479
@param last_update_time last update time
480-
"""
480+
"""

fabric_cf/actor/core/common/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ class Constants:
173173
PROPERTY_CONF_O_AUTH_TRL_REFRESH = "trl-refresh"
174174
PROPERTY_CONF_O_AUTH_VERIFY_EXP = "verify-exp"
175175

176+
CONFIG_SECTION_CORE_API = "core_api"
177+
PROPERTY_CONF_HOST = "host"
178+
176179
CONFIG_SECTION_SMTP = "smtp"
177180

178181
CONFIG_SECTION_DATABASE = "database"
@@ -275,6 +278,7 @@ class Constants:
275278
CLAIMS_PROJECTS = "projects"
276279
UUID = "uuid"
277280
TAGS = "tags"
281+
TOKEN = "token"
278282
TOKEN_HASH = "token_hash"
279283
PROJECT_ID = "project_id"
280284
USERS = "users"

fabric_cf/actor/core/container/globals.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import logging
3636
import os
3737

38+
from fabric_cf.actor.core.util.quota_mgr import QuotaMgr
3839
from fim.graph.neo4j_property_graph import Neo4jGraphImporter
3940
from fim.graph.resources.abc_arm import ABCARMPropertyGraph
4041
from fss_utils.jwt_validate import JWTValidator
@@ -74,6 +75,7 @@ def __init__(self):
7475
self.lock = threading.Lock()
7576
self.jwt_validator = None
7677
self.token_validator = None
78+
self.quota_mgr = None
7779

7880
def make_logger(self):
7981
"""
@@ -193,6 +195,12 @@ def load_validators(self):
193195
refresh_period=timedelta(hours=t.hour, minutes=t.minute, seconds=t.second),
194196
jwt_validator=self.jwt_validator)
195197

198+
core_api = self.config.get_core_api_config()
199+
if core_api.get("enable", False):
200+
self.quota_mgr = QuotaMgr(core_api_host=core_api.get(Constants.PROPERTY_CONF_HOST),
201+
token=core_api.get(Constants.TOKEN, ""),
202+
logger=self.log)
203+
196204
def load_config(self):
197205
"""
198206
Load the configuration
@@ -211,6 +219,9 @@ def get_jwt_validator(self) -> JWTValidator:
211219
def get_token_validator(self) -> TokenValidator:
212220
return self.token_validator
213221

222+
def get_quota_mgr(self) -> QuotaMgr:
223+
return self.quota_mgr
224+
214225
def get_container(self) -> ABCActorContainer:
215226
"""
216227
Get the container

fabric_cf/actor/core/kernel/kernel.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import threading
2727
import time
2828
import traceback
29+
from datetime import datetime, timezone
2930

3031
from typing import List, Dict
3132

@@ -34,7 +35,7 @@
3435
from fabric_cf.actor.core.apis.abc_delegation import ABCDelegation
3536
from fabric_cf.actor.core.apis.abc_policy import ABCPolicy
3637
from fabric_cf.actor.core.common.constants import Constants
37-
from fabric_cf.actor.core.common.event_logger import EventLogger, EventLoggerSingleton
38+
from fabric_cf.actor.core.common.event_logger import EventLoggerSingleton
3839
from fabric_cf.actor.core.common.exceptions import ReservationNotFoundException, DelegationNotFoundException, \
3940
KernelException
4041
from fabric_cf.actor.core.kernel.authority_reservation import AuthorityReservation

fabric_cf/actor/core/kernel/reservation_client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,16 +1557,16 @@ def validate_redeem(self):
15571557

15581558
def add_redeem_predecessor(self, *, reservation: ABCReservationMixin, filters: dict = None):
15591559
if reservation.get_reservation_id() not in self.redeem_predecessors:
1560-
state = PredecessorState(reservation=reservation)
1560+
state = PredecessorState(reservation=reservation, filters=filters)
15611561
self.redeem_predecessors[reservation.get_reservation_id()] = state
15621562

15631563
def remove_redeem_predecessor(self, *, rid: ID):
15641564
if rid in self.redeem_predecessors:
15651565
self.redeem_predecessors.pop(rid)
15661566

1567-
def add_join_predecessor(self, *, predecessor):
1567+
def add_join_predecessor(self, *, predecessor: ABCReservationMixin, filters: dict = None):
15681568
if predecessor.get_reservation_id() not in self.redeem_predecessors:
1569-
state = PredecessorState(reservation=predecessor)
1569+
state = PredecessorState(reservation=predecessor, filters=filters)
15701570
self.join_predecessors[predecessor.get_reservation_id()] = state
15711571

15721572
def get_redeem_predecessors(self) -> List[PredecessorState]:

fabric_cf/actor/core/manage/actor_management_object.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
from datetime import datetime, timezone
3030
from typing import TYPE_CHECKING, List, Dict, Tuple
3131

32+
from fabric_mb.message_bus.messages.lease_reservation_avro import LeaseReservationAvro
33+
3234
from fabric_cf.actor.fim.fim_helper import FimHelper
3335
from fabric_mb.message_bus.messages.reservation_mng import ReservationMng
3436
from fabric_mb.message_bus.messages.result_delegation_avro import ResultDelegationAvro
@@ -902,4 +904,4 @@ def build_broker_query_model(self, level_0_broker_query_model: str, level: int,
902904
end=end, includes=includes, excludes=excludes)
903905
except Exception as e:
904906
self.logger.error(f"Exception occurred build_broker_query_model e: {e}")
905-
self.logger.error(traceback.format_exc())
907+
self.logger.error(traceback.format_exc())

fabric_cf/actor/core/manage/client_actor_management_object_helper.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@
4343

4444
from fabric_cf.actor.core.apis.abc_actor_runnable import ABCActorRunnable
4545
from fabric_cf.actor.core.apis.abc_controller_reservation import ABCControllerReservation
46-
from fabric_cf.actor.core.apis.abc_reservation_mixin import ABCReservationMixin
47-
from fabric_cf.actor.core.common.constants import Constants, ErrorCodes
46+
from fabric_cf.actor.core.common.constants import ErrorCodes
4847
from fabric_cf.actor.core.common.exceptions import ManageException
4948
from fabric_cf.actor.core.kernel.reservation_client import ClientReservationFactory
5049
from fabric_cf.actor.core.kernel.reservation_states import ReservationStates, ReservationPendingStates
@@ -635,4 +634,4 @@ def run(self):
635634
result.set_message(ErrorCodes.ErrorInternalError.interpret(exception=e))
636635
result = ManagementObject.set_exception_details(result=result, e=e)
637636

638-
return result
637+
return result

fabric_cf/actor/core/plugins/db/actor_database.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
import time
2929
import traceback
3030
from datetime import datetime
31-
from typing import List, Union, Tuple, Dict
31+
from typing import List, Union, Dict
32+
3233

3334
from fabric_cf.actor.core.apis.abc_actor_mixin import ABCActorMixin, ActorType
3435
from fabric_cf.actor.core.apis.abc_broker_proxy import ABCBrokerProxy

fabric_cf/actor/core/policy/broker_simpler_units_policy.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,8 +1075,20 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF
10751075
try:
10761076
if operation == ReservationOperation.Extend:
10771077
rset = reservation.get_resources()
1078+
duration = Term.delta(reservation.get_term().get_end_time(), term.get_end_time())
10781079
else:
10791080
rset = reservation.get_requested_resources()
1081+
duration = term.get_full_length()
1082+
1083+
from fabric_cf.actor.core.container.globals import GlobalsSingleton
1084+
if GlobalsSingleton.get().get_quota_mgr():
1085+
status, error_msg = GlobalsSingleton.get().get_quota_mgr().enforce_quota_limits(reservation=reservation,
1086+
duration=duration)
1087+
self.logger.info(f"Quota enforcement status: {status}, error: {error_msg}")
1088+
# TODO: enable enforcement action later
1089+
#if not status:
1090+
# return status, node_id_to_reservations, error_msg
1091+
10801092
needed = rset.get_units()
10811093

10821094
# for network node slivers
@@ -1115,6 +1127,11 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF
11151127
if node_id_to_reservations.get(node_id, None) is None:
11161128
node_id_to_reservations[node_id] = ReservationSet()
11171129
node_id_to_reservations[node_id].add(reservation=reservation)
1130+
1131+
from fabric_cf.actor.core.container.globals import GlobalsSingleton
1132+
if GlobalsSingleton.get().get_quota_mgr():
1133+
GlobalsSingleton.get().get_quota_mgr().update_quota(reservation=reservation, duration=duration)
1134+
11181135
self.logger.debug(f"Ticket Inventory returning: True {error_msg}")
11191136
return True, node_id_to_reservations, error_msg
11201137
except Exception as e:
@@ -1204,6 +1221,13 @@ def issue_ticket(self, *, reservation: ABCBrokerReservation, units: int, rtype:
12041221
return reservation
12051222

12061223
def release(self, *, reservation):
1224+
duration = reservation.get_term().get_remaining_length()
1225+
if duration > 0:
1226+
from fabric_cf.actor.core.container.globals import GlobalsSingleton
1227+
if GlobalsSingleton.get().get_quota_mgr():
1228+
GlobalsSingleton.get().get_quota_mgr().update_quota(reservation=reservation,
1229+
duration=duration)
1230+
12071231
if isinstance(reservation, ABCBrokerReservation):
12081232
self.logger.debug("Broker reservation")
12091233
super().release(reservation=reservation)

fabric_cf/actor/core/policy/inventory_for_type.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def free(self, *, count: int, request: dict = None, resource: dict = None) -> di
5959
"""
6060

6161
@staticmethod
62-
def _get_allocated_sliver(reservation: ABCReservationMixin) -> BaseSliver:
62+
def get_allocated_sliver(reservation: ABCReservationMixin) -> BaseSliver:
6363
"""
6464
Retrieve the allocated sliver from the reservation.
6565
@@ -70,3 +70,5 @@ def _get_allocated_sliver(reservation: ABCReservationMixin) -> BaseSliver:
7070
return reservation.get_approved_resources().get_sliver()
7171
if (reservation.is_active() or reservation.is_ticketed()) and reservation.get_resources() is not None:
7272
return reservation.get_resources().get_sliver()
73+
if (reservation.is_closed()) and reservation.get_resources() is not None:
74+
return reservation.get_resources().get_sliver()

fabric_cf/actor/core/policy/network_node_inventory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def check_capacities(*, rid: ID, requested_capacities: Capacities, delegated: De
6969
if rid == reservation.get_reservation_id():
7070
continue
7171
# For Active or Ticketed or Ticketing reservations; reduce the counts from available
72-
resource_sliver = InventoryForType._get_allocated_sliver(reservation=reservation)
72+
resource_sliver = InventoryForType.get_allocated_sliver(reservation=reservation)
7373

7474
if resource_sliver is not None and isinstance(resource_sliver, NodeSliver):
7575
logger.debug(
@@ -380,7 +380,7 @@ def __exclude_components_for_existing_reservations(*, rid: ID, graph_node: NodeS
380380
(operation == ReservationOperation.Extend or not reservation.is_ticketed()):
381381
continue
382382
# For Active or Ticketed or Ticketing reservations; reduce the counts from available
383-
allocated_sliver = InventoryForType._get_allocated_sliver(reservation=reservation)
383+
allocated_sliver = InventoryForType.get_allocated_sliver(reservation=reservation)
384384

385385
if reservation.is_extending_ticket() and reservation.get_requested_resources() is not None and \
386386
reservation.get_requested_resources().get_sliver() is not None:

fabric_cf/actor/core/policy/network_service_inventory.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def __exclude_allocated_vlans(self, *, rid: ID, available_vlan_range: List[int],
7676
continue
7777

7878
# For Active or Ticketed or Ticketing reservations; reduce the counts from available
79-
allocated_sliver = self._get_allocated_sliver(reservation=reservation)
79+
allocated_sliver = self.get_allocated_sliver(reservation=reservation)
8080

8181
self.logger.debug(
8282
f"Existing res# {reservation.get_reservation_id()} state:{reservation.get_state()} "
@@ -278,7 +278,7 @@ def allocate_vnic(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns
278278
continue
279279

280280
# For Active or Ticketed or Ticketing reservations; reduce the counts from available
281-
allocated_sliver = self._get_allocated_sliver(reservation=reservation)
281+
allocated_sliver = self.get_allocated_sliver(reservation=reservation)
282282

283283
self.logger.debug(f"Existing res# {reservation.get_reservation_id()} "
284284
f"allocated: {allocated_sliver}")
@@ -413,7 +413,7 @@ def _exclude_allocated_subnets(self, *, subnet_list: List, requested_ns_type: st
413413
if rid == reservation.get_reservation_id():
414414
continue
415415

416-
allocated_sliver = self._get_allocated_sliver(reservation)
416+
allocated_sliver = self.get_allocated_sliver(reservation)
417417
if allocated_sliver is None:
418418
continue
419419

fabric_cf/actor/core/time/term.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,18 @@ def get_full_length(self) -> int:
331331

332332
return end_ms - start_ms + 1
333333

334+
def get_remaining_length(self) -> int:
335+
"""
336+
Returns the length of remaining term in milliseconds. The length of a term is the
337+
number of milliseconds in the closed interval [now, end]
338+
@returns term length
339+
"""
340+
now = datetime.now(timezone.utc)
341+
current_ms = ActorClock.to_milliseconds(when=now)
342+
end_ms = ActorClock.to_milliseconds(when=self.end_time)
343+
344+
return end_ms - current_ms + 1
345+
334346
def get_length(self) -> int:
335347
"""
336348
Returns the length of a term in milliseconds. The length of a term is the

0 commit comments

Comments
 (0)