diff --git a/proto/load_generator.proto b/proto/load_generator.proto index 395afbb883d4c014432a056ada5ca5bb467738a3..7d0070c66f1104d9903950fb8b59f64e3ec42f71 100644 --- a/proto/load_generator.proto +++ b/proto/load_generator.proto @@ -64,6 +64,7 @@ message Parameters { message Status { Parameters parameters = 1; uint64 num_generated = 2; - bool infinite_loop = 3; - bool running = 4; + uint64 num_released = 3; + bool infinite_loop = 4; + bool running = 5; } diff --git a/src/load_generator/load_gen/RequestGenerator.py b/src/load_generator/load_gen/RequestGenerator.py index ab8f7e30e7e9de61bcfa7f5c52b7a09deb00ba2a..3a52b3b322bfefe60e7c5c8d3eed585b92b40353 100644 --- a/src/load_generator/load_gen/RequestGenerator.py +++ b/src/load_generator/load_gen/RequestGenerator.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, json, random, re, threading +import logging, json, random, re, threading, uuid from typing import Dict, Optional, Set, Tuple from common.proto.context_pb2 import Empty, IsolationLevelEnum, TopologyId from common.tools.grpc.Tools import grpc_message_to_json @@ -54,6 +54,7 @@ class RequestGenerator: self._parameters = parameters self._lock = threading.Lock() self._num_generated = 0 + self._num_released = 0 self._available_device_endpoints : Dict[str, Set[str]] = dict() self._used_device_endpoints : Dict[str, Dict[str, str]] = dict() self._endpoint_ids_to_types : Dict[Tuple[str, str], str] = dict() @@ -65,6 +66,9 @@ class RequestGenerator: @property def num_generated(self): return self._num_generated + @property + def num_released(self): return self._num_released + @property def infinite_loop(self): return self._parameters.num_requests == 0 @@ -192,23 +196,18 @@ class RequestGenerator: if not self.infinite_loop and (self._num_generated >= self._parameters.num_requests): LOGGER.info('Generation Done!') return True, None, None # completed - self._num_generated += 1 - num_request = self._num_generated - #request_uuid = str(uuid.uuid4()) - request_uuid = 'svc_{:d}'.format(num_request) - - # choose request type + request_uuid = str(uuid.uuid4()) request_type = random.choice(self._parameters.request_types) if request_type in { RequestType.SERVICE_L2NM, RequestType.SERVICE_L3NM, RequestType.SERVICE_TAPI, RequestType.SERVICE_MW }: - return False, self._compose_service(num_request, request_uuid, request_type), request_type + return False, self._compose_service(request_uuid, request_type), request_type elif request_type in {RequestType.SLICE_L2NM, RequestType.SLICE_L3NM}: - return False, self._compose_slice(num_request, request_uuid, request_type), request_type + return False, self._compose_slice(request_uuid, request_type), request_type - def _compose_service(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: + def _compose_service(self, request_uuid : str, request_type : str) -> Optional[Dict]: # choose source endpoint src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if request_type in {RequestType.SERVICE_TAPI} else None src = self._use_device_endpoint(request_uuid, request_type, endpoint_types=src_endpoint_types) @@ -237,6 +236,10 @@ class RequestGenerator: self._release_device_endpoint(src_device_uuid, src_endpoint_uuid) return None + with self._lock: + self._num_generated += 1 + num_request = self._num_generated + # compose endpoints dst_device_uuid,dst_endpoint_uuid = dst endpoint_ids = [ @@ -383,7 +386,7 @@ class RequestGenerator: return json_service_l2nm_planned( request_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules) - def _compose_slice(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: + def _compose_slice(self, request_uuid : str, request_type : str) -> Optional[Dict]: # choose source endpoint src = self._use_device_endpoint(request_uuid, request_type) if src is None: @@ -404,6 +407,10 @@ class RequestGenerator: self._release_device_endpoint(src_device_uuid, src_endpoint_uuid) return None + with self._lock: + self._num_generated += 1 + num_request = self._num_generated + # compose endpoints dst_device_uuid,dst_endpoint_uuid = dst endpoint_ids = [ @@ -505,8 +512,15 @@ class RequestGenerator: device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] self._release_device_endpoint(device_uuid, endpoint_uuid) + + with self._lock: + self._num_released += 1 + elif 'slice_id' in json_request: for endpoint_id in json_request['slice_endpoint_ids']: device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] self._release_device_endpoint(device_uuid, endpoint_uuid) + + with self._lock: + self._num_released += 1 diff --git a/src/load_generator/load_gen/RequestScheduler.py b/src/load_generator/load_gen/RequestScheduler.py index 08876e29fa7da3f50ba78553a6aee81f0add7b56..340a5411bacee7b0aeb495963cdf65fbb5d14389 100644 --- a/src/load_generator/load_gen/RequestScheduler.py +++ b/src/load_generator/load_gen/RequestScheduler.py @@ -57,6 +57,9 @@ class RequestScheduler: @property def num_generated(self): return min(self._generator.num_generated, self._parameters.num_requests) + @property + def num_released(self): return min(self._generator.num_released, self._parameters.num_requests) + @property def infinite_loop(self): return self._generator.infinite_loop diff --git a/src/load_generator/service/LoadGeneratorServiceServicerImpl.py b/src/load_generator/service/LoadGeneratorServiceServicerImpl.py index 41c12d8e461364c5994b9ba0989c8d4241d3e3fe..9f12f34920fda69ba55963876e96f51a8256537c 100644 --- a/src/load_generator/service/LoadGeneratorServiceServicerImpl.py +++ b/src/load_generator/service/LoadGeneratorServiceServicerImpl.py @@ -73,6 +73,7 @@ class LoadGeneratorServiceServicerImpl(LoadGeneratorServiceServicer): status = Status() status.num_generated = self._scheduler.num_generated + status.num_released = self._scheduler.num_released status.infinite_loop = self._scheduler.infinite_loop status.running = self._scheduler.running diff --git a/src/webui/service/load_gen/forms.py b/src/webui/service/load_gen/forms.py index 8092b3193563df52e9c538e20890835f799fe8ee..e0d11800cf9fbd9b0e195de7aa85eede272fe28e 100644 --- a/src/webui/service/load_gen/forms.py +++ b/src/webui/service/load_gen/forms.py @@ -24,6 +24,7 @@ DEFAULT_E2E_LATENCY_MS = '5.0..100.00' class LoadGenForm(FlaskForm): num_requests = IntegerField('Num Requests', default=100, validators=[DataRequired(), NumberRange(min=0)]) num_generated = IntegerField('Num Generated', default=0, render_kw={'readonly': True}) + num_released = IntegerField('Num Released', default=0, render_kw={'readonly': True}) request_type_service_l2nm = BooleanField('Service L2NM', default=False) request_type_service_l3nm = BooleanField('Service L3NM', default=False) diff --git a/src/webui/service/load_gen/routes.py b/src/webui/service/load_gen/routes.py index 1b4f9c6ba971c17fc3ac9216aad6cc39b20c8151..f05f57f6d5aab83c0752dda15e0b858c9a4d53a3 100644 --- a/src/webui/service/load_gen/routes.py +++ b/src/webui/service/load_gen/routes.py @@ -80,6 +80,7 @@ def home(): set_properties(form.request_type_slice_l2nm , _request_type_slice_l2nm , disabled=status.running) set_properties(form.request_type_slice_l3nm , _request_type_slice_l3nm , disabled=status.running) set_properties(form.num_generated , status.num_generated , disabled=True) + set_properties(form.num_released , status.num_released , disabled=True) set_properties(form.infinite_loop , status.infinite_loop , disabled=True) set_properties(form.running , status.running , disabled=True) diff --git a/src/webui/service/templates/load_gen/home.html b/src/webui/service/templates/load_gen/home.html index 046459acf12068b2cb9d8529d4454c36609613d5..5bedf66fad1fa2d1b5e38e3866acd95347c9559b 100644 --- a/src/webui/service/templates/load_gen/home.html +++ b/src/webui/service/templates/load_gen/home.html @@ -53,6 +53,21 @@ </div> <br /> + <div class="row mb-3"> + {{ form.num_released.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.num_released.errors %} + {{ form.num_released(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.num_released.errors %}<span>{{ error }}</span>{% endfor %} + </div> + {% else %} + {{ form.num_released(class="form-control") }} + {% endif %} + </div> + </div> + <br /> + <div class="row mb-3"> <div class="col-sm-2 col-form-label">Service Types:</div> <div class="col-sm-10">