diff --git a/proto/device.proto b/proto/device.proto index 005521abf091e9aca4abcc258d4cf3710088274d..40803e8e23a6c7e6267e9d5334d4a33abfa9450a 100644 --- a/proto/device.proto +++ b/proto/device.proto @@ -25,6 +25,7 @@ service DeviceService { rpc GetInitialConfig (context.DeviceId ) returns (context.DeviceConfig ) {} rpc MonitorDeviceKpi (MonitoringSettings ) returns (context.Empty ) {} rpc SSETelemetrySubscribe(monitoring.SSEMonitoringSubscriptionConfig) returns (monitoring.SSEMonitoringSubscriptionResponse ) {} + rpc UpdateDeviceInventory(context.DeviceId ) returns (context.Empty ) {} } message MonitoringSettings { diff --git a/src/context/service/database/Device.py b/src/context/service/database/Device.py index bff03a0a5b85b4617d4bc4917cebb9b3d7bea680..89bb63d6c16b18809100419350cf41626c2ea77e 100644 --- a/src/context/service/database/Device.py +++ b/src/context/service/database/Device.py @@ -31,6 +31,7 @@ from .models.DeviceModel import DeviceModel from .models.EndPointModel import EndPointModel from .models.ComponentModel import ComponentModel from .models.TopologyModel import TopologyDeviceModel, TopologyModel +from .models.ConfigRuleModel import DeviceConfigRuleModel, ConfigRuleKindEnum from .models.enums.DeviceDriver import grpc_to_enum__device_driver from .models.enums.DeviceOperationalStatus import grpc_to_enum__device_operational_status from .models.enums.KpiSampleType import grpc_to_enum__kpi_sample_type @@ -243,6 +244,9 @@ def device_set(db_engine : Engine, messagebroker : MessageBroker, request : Devi device_topology_ids = [obj.dump_id() for obj in device_topologies] #LOGGER.warning('device_topology_ids={:s}'.format(str(device_topology_ids))) + # Delete old components of the device to make sure they are fully updated + session.query(ComponentModel).filter_by(device_uuid=device_uuid).delete() + updated_components = False if len(components_data) > 0: @@ -261,6 +265,13 @@ def device_set(db_engine : Engine, messagebroker : MessageBroker, request : Devi component_updates = session.execute(stmt).fetchall() updated_components = any([(updated_at > created_at) for created_at,updated_at in component_updates]) + # Delete old /inventory config rules of the device to make sure they are fully updated + session.query(DeviceConfigRuleModel).filter( + DeviceConfigRuleModel.device_uuid == device_uuid, + DeviceConfigRuleModel.kind == ConfigRuleKindEnum.CUSTOM, + DeviceConfigRuleModel.data.contains('/inventory') + ).delete(synchronize_session=False) + changed_config_rules = upsert_config_rules(session, config_rules, device_uuid=device_uuid) return updated or updated_endpoints or updated_components or changed_config_rules, device_topology_ids diff --git a/src/device/client/DeviceClient.py b/src/device/client/DeviceClient.py index b15589ac03c1931c93d62e356df1dc0e207f6362..549e9a5a3a1a573e9672cf948d4d8e1b41e2627e 100644 --- a/src/device/client/DeviceClient.py +++ b/src/device/client/DeviceClient.py @@ -113,3 +113,11 @@ class DeviceClient: response = self.stub.SSETelemetrySubscribe(request) LOGGER.debug('SSETelemetrySubscribe result: {:s}'.format(grpc_message_to_json_string(response))) return response + + @RETRY_DECORATOR + def UpdateDeviceInventory(self, request : DeviceId) -> Empty: + LOGGER.debug('UpdateDeviceInventory request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.UpdateDeviceInventory(request) + LOGGER.debug('UpdateDeviceInventory result: {:s}'.format(grpc_message_to_json_string(response))) + return response + diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index 294f292770ef66dabaf2381a0aa8daa7ad01d85d..9f85f38ba32a1f3d1a392541752f6ae95b2b7f29 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -21,7 +21,7 @@ from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, safe_a from common.method_wrappers.ServiceExceptions import NotFoundException, OperationFailedException from common.proto.context_pb2 import ( Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link, - OpticalConfig, OpticalConfigId + OpticalConfig, OpticalConfigId, ConfigActionEnum ) from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceServicer @@ -37,7 +37,7 @@ from .Tools import ( check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules, get_device_controller_uuid, populate_config_rules, populate_endpoint_monitoring_resources, populate_endpoints, populate_initial_config_rules, - subscribe_kpi, unsubscribe_kpi, update_endpoints, update_sap_id, + subscribe_kpi, unsubscribe_kpi, update_endpoints, update_sap_id, _raw_config_rules_to_grpc ) LOGGER = logging.getLogger(__name__) @@ -445,3 +445,61 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): ) return SSEMonitoringSubscriptionResponse() + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def UpdateDeviceInventory(self, request : DeviceId, context : grpc.ServicerContext) -> Empty: + device_uuid = request.device_uuid.uuid + self.mutex_queues.wait_my_turn(device_uuid) + try: + context_client = ContextClient() + device = get_device(context_client, device_uuid, rw_copy=True) + if device is None: + raise NotFoundException('Device', device_uuid) + + driver : _Driver = get_driver(self.driver_instance_cache, device) + if driver is None: + msg = ERROR_MISSING_DRIVER.format(device_uuid=str(device_uuid)) + raise OperationFailedException('UpdateDeviceInventory', extra_details=msg) + + # Clear and populate endpoints again from the driver + del device.device_endpoints[:] + new_sub_devices = dict() + new_sub_links = dict() + sorted_sub_device_uuids = list() + new_optical_configs = dict() + errors = populate_endpoints( + device, driver, self.monitoring_loops, new_sub_devices, sorted_sub_device_uuids, + new_sub_links, new_optical_configs + ) + if len(errors) > 0: + raise OperationFailedException('UpdateDeviceInventory', extra_details=errors) + + from device.service.driver_api._Driver import RESOURCE_INVENTORY + results_getconfig = driver.GetConfig([RESOURCE_INVENTORY]) + + # Filter out existing /inventory rules to prevent duplicates or leftovers + new_rules = [] + for rule in device.device_config.config_rules: + if rule.WhichOneof('config_rule') == 'custom': + if '/inventory' in rule.custom.resource_key: + continue + new_rules.append(rule) + del device.device_config.config_rules[:] + device.device_config.config_rules.extend(new_rules) + + # Convert new inventory rules and update device config + errors = _raw_config_rules_to_grpc( + device_uuid, device.device_config, 'Error getting inventory resource {resource_key} on device {device_uuid}: {error}', + ConfigActionEnum.CONFIGACTION_SET, results_getconfig + ) + + if len(errors) > 0: + raise OperationFailedException('UpdateDeviceInventory', extra_details=errors) + + context_client.SetDevice(device) + context_client.close() + return Empty() + except Exception as e: + LOGGER.exception('Error updating inventory of device {:s}'.format(str(device_uuid))) + raise e + + diff --git a/src/tests/ecoc22/.gitlab-ci.yml b/src/tests/ecoc22/.gitlab-ci.yml index 630dcd40a5169050a77abd8982333016e68a063f..56db69560178f467e149f2d8d64311129a209cd4 100644 --- a/src/tests/ecoc22/.gitlab-ci.yml +++ b/src/tests/ecoc22/.gitlab-ci.yml @@ -66,7 +66,6 @@ end2end_test ecoc22: #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/deviceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="frontend").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/pathcompservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/serviceservice.yaml - #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/sliceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/nbiservice.yaml - source src/tests/${TEST_NAME}/deploy_specs.sh diff --git a/src/tests/ofc22/.gitlab-ci.yml b/src/tests/ofc22/.gitlab-ci.yml index 79e0a815f024b27905cb7e2e91594e17ebbd85ff..84b94c02cec24c300162e82ca1b9fd2965b9ea0c 100644 --- a/src/tests/ofc22/.gitlab-ci.yml +++ b/src/tests/ofc22/.gitlab-ci.yml @@ -66,7 +66,6 @@ end2end_test ofc22: #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/deviceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="frontend").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/pathcompservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/serviceservice.yaml - #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/sliceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/nbiservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/monitoringservice.yaml diff --git a/src/tests/ofc24/.gitlab-ci.yml b/src/tests/ofc24/.gitlab-ci.yml index 213dafba05ae01b97b71f149758016afc1a711c4..7f8ec8bde7baa27f8ab7c068fcdc407f804f61a9 100644 --- a/src/tests/ofc24/.gitlab-ci.yml +++ b/src/tests/ofc24/.gitlab-ci.yml @@ -105,7 +105,6 @@ end2end_test ofc24: #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/deviceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="frontend").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/pathcompservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/serviceservice.yaml - #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/sliceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/nbiservice.yaml - source src/tests/${TEST_NAME}/deploy_specs.sh diff --git a/src/tests/ofc25/.gitlab-ci.yml b/src/tests/ofc25/.gitlab-ci.yml index 81529ec091ec02f6173cab7d00707d9a2b243040..4777cf4dd755541f73c05fce57b28b42bc3eed9f 100644 --- a/src/tests/ofc25/.gitlab-ci.yml +++ b/src/tests/ofc25/.gitlab-ci.yml @@ -102,7 +102,6 @@ end2end_test ofc25: #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/deviceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="frontend").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/pathcompservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/serviceservice.yaml - #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/sliceservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/nbiservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/opticalcontrollerservice.yaml #- yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/e2e_orchestratorservice.yaml diff --git a/src/tests/ofc25/deploy_all_in_one.sh b/src/tests/ofc25/deploy_all_in_one.sh index bf37a34fe2c8eaa18c60e91704334e21fff52438..d41b085a49c68fa68111edb014f4e00248f431d9 100755 --- a/src/tests/ofc25/deploy_all_in_one.sh +++ b/src/tests/ofc25/deploy_all_in_one.sh @@ -41,7 +41,6 @@ kubectl get pods --all-namespaces #yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/deviceservice.yaml #yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="frontend").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/pathcompservice.yaml #yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/serviceservice.yaml -#yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/sliceservice.yaml #yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/nbiservice.yaml #yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/e2eorchestratorservice.yaml #yq -i '((select(.kind=="Deployment").spec.template.spec.containers.[] | select(.name=="server").env.[]) | select(.name=="LOG_LEVEL").value) |= "DEBUG"' manifests/vntmservice.yaml diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index 23a19278caeb86a3d8769d632b5725cf6ec5ebbe..e1cfb000a97e46e6ccda2ea4eb5c16dac2b8ea08 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -180,7 +180,20 @@ def detail(device_uuid: str): return render_template( 'device/detail.html', device=device_obj, dde=DeviceDriverEnum, dose=DeviceOperationalStatusEnum) - + +@device.route('detail//update_inventory', methods=['GET', 'POST']) +def update_inventory(device_uuid: str): + try: + device_id = DeviceId() + device_id.device_uuid.uuid = device_uuid + device_client.connect() + device_client.UpdateDeviceInventory(device_id) + device_client.close() + flash(f'Inventory of device "{device_uuid}" was successfully updated.', 'success') + except Exception as e: + flash(f'Problem updating inventory of device "{device_uuid}": {str(e)}', 'danger') + return redirect(url_for('device.detail', device_uuid=device_uuid)) + @device.route('inventory/', methods=['GET', 'POST']) def inventory(device_uuid: str): context_client.connect() diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html index 75952ba1d8c93b28e79976ec4b12b8b24937c441..c2b633f01875a9ce4acf299732e95f88710434d7 100644 --- a/src/webui/service/templates/device/detail.html +++ b/src/webui/service/templates/device/detail.html @@ -39,6 +39,12 @@ Delete device +