diff --git a/MANIFEST.in b/MANIFEST.in index 60b0a16..e857479 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,2 @@ -include ./VERSION -include ./src/generated/*.proto -include ./src/generated/*.pyi +include ./keysight_chakra/protos/*.proto +include ./keysight_chakra/generated/*.pyi diff --git a/Makefile b/Makefile index 3066759..def0c72 100644 --- a/Makefile +++ b/Makefile @@ -8,15 +8,15 @@ env: ## install env requirements .PHONY: build GENERATED_DIR := ./keysight_chakra/generated build: ## compile all .proto files and generate artifacts - curl -L -o ./protos/et_def.proto https://raw.githubusercontent.com/mlcommons/chakra/main/schema/protobuf/et_def.proto + curl -L -o ./keysight_chakra/protos/et_def.proto https://raw.githubusercontent.com/mlcommons/chakra/main/schema/protobuf/et_def.proto rm -rf $(GENERATED_DIR) || true mkdir -p $(GENERATED_DIR) python3 -m grpc_tools.protoc \ - --proto_path=./protos \ + --proto_path=./keysight_chakra/protos \ --python_out=$(GENERATED_DIR) \ --pyi_out=$(GENERATED_DIR) \ --grpc_python_out=$(GENERATED_DIR) \ - et_def.proto infra.proto bind.proto service.proto + et_def.proto infra.proto annotate.proto service.proto python3 -m pip uninstall -y keysight-chakra python3 setup.py bdist_wheel python3 -m pip install --no-cache . diff --git a/VERSION b/VERSION index 7dea76e..9084fa2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.1 +1.1.0 diff --git a/protos/bind.proto b/keysight_chakra/protos/annotate.proto similarity index 68% rename from protos/bind.proto rename to keysight_chakra/protos/annotate.proto index d2c56bf..b7cafac 100644 --- a/protos/bind.proto +++ b/keysight_chakra/protos/annotate.proto @@ -1,23 +1,23 @@ -// bind.proto +// annotate.proto // -// A data model to describe binding infrastructure paths to external +// A data model to annotate infrastructure paths with external // application information. // -// Some examples of binding third party information to infrastructure paths: +// Thesea are some examples of annotating infrastructure paths: // -// binding = Binding( +// annotation = Annotation( // device_instance="pod_switch.0", // data=Data( // name="routing", // value="routing configuration information in a third party format")) // -// binding = Binding( +// annotation = Annotation( // device_instance_index_component_index="host.0.nic.0", // data=Data( // name="mellanox_config", // value="additional nic configuration information in a third party format")) // -// binding = Binding( +// annotation = Annotation( // infrastructure="ranks", // data=Data( // name="rank_to_host_map", @@ -26,12 +26,12 @@ syntax = "proto3"; -package bind; +package annotate; import "google/protobuf/any.proto"; -// Data message allows a user to provide data outside of the scope of the -// infrastructure graph. +// Data message allows a user to provide data that is outside of the scope of +// the infrastructure graph. message Data { // Use this field to provide descriptive information about the message // that is packed into the value field. @@ -59,45 +59,45 @@ message Data { google.protobuf.Any value = 2; } -// The BindTarget message -message BindTarget { - // The location of infrastructure that matches the binding type format +// The Target message +message Target { + // The location of infrastructure that is associated with the annotation data oneof infrastructure_path { - // the binding is global to the infrastructure and the value provided here - // is for informational purposes only + // the annotation is global to the infrastructure and the value provided + // here is for informational purposes only string infrastructure = 1; - // binding is specific to an Infrastructure.inventory.device.name + // the annotation is specific to an Infrastructure.inventory.device.name // example: dgx string device = 2; - // binding is specific to an Infrastructure.inventory.device.name + // the annotation is specific to an Infrastructure.inventory.device.name // example: dgx.npu string device_component = 3; - // binding is specific to an Infrastructure.inventory.device.name + // the annotation is specific to an Infrastructure.inventory.device.name // example: dgx.npu.0 string device_component_index = 4; - // binding is specific to an instance of + // the annotation is specific to an instance of // Infrastructure.device_instances.name // example: host string device_instance = 5; - // binding is specific to an instance of + // the annotation is specific to an instance of // Infrastructure.device_instances.name // and an index < Infrastructure.device_instances.count // example: host.1 string device_instance_index = 6; - // binding is specific to an instance of + // the annotation is specific to an instance of // Infrastructure.device_instances.name // and an index < Infrastructure.device_instances.count // and an Infrastructure.inventory.device.component.name // example: host.1.npu string device_instance_index_component = 7; - // binding is specific to an instance of + // the annotation is specific to an instance of // Infrastructure.device_instances.name // and an index < Infrastructure.device_instances.count // and an Infrastructure.inventory.device.component.name @@ -107,22 +107,17 @@ message BindTarget { } } -// The Binding message offers the option of binding different types of logical -// Infrastructure endpoints to any type of user defined data. +// The Annotation message allows for associating different types of +// logical Infrastructure endpoints to any type of user defined data. // // The format allows for data to be applied at a macro level such as // all devices or at a micro level such as an individual component in a // specific device instance. -message Binding { - // targets is a list of BindTarget messages that share the same +message Annotation { + // targets is a list of Target messages that share the same // user defined information stored in the data field - repeated BindTarget targets = 1; + repeated Target targets = 1; // the data field accomodates any type of user defined information Data data = 100; } - -message Bindings { - // A list of user defined information for specific endpoints - repeated Binding bindings = 1; -} diff --git a/protos/et_def.proto b/keysight_chakra/protos/et_def.proto similarity index 100% rename from protos/et_def.proto rename to keysight_chakra/protos/et_def.proto diff --git a/protos/infra.proto b/keysight_chakra/protos/infra.proto similarity index 100% rename from protos/infra.proto rename to keysight_chakra/protos/infra.proto diff --git a/protos/service.proto b/keysight_chakra/protos/service.proto similarity index 72% rename from protos/service.proto rename to keysight_chakra/protos/service.proto index bed96f2..3ab0c8c 100644 --- a/protos/service.proto +++ b/keysight_chakra/protos/service.proto @@ -1,17 +1,17 @@ // service.proto // -// Service and rpcs for infrastructure and bindings +// Service and rpcs for infrastructure and annotations syntax = "proto3"; package service; import "infra.proto"; -import "bind.proto"; +import "annotate.proto"; message ValidationRequest { infra.Infrastructure infrastructure = 1; - bind.Bindings bindings = 2; + repeated annotate.Annotation annotations = 2; } message ValidationError { @@ -29,9 +29,9 @@ message ValidationError { // the following cases fall under this type: // connection structure is incorrectly formatted // connection pieces are not present in the inventory or device instances - // binding infrastructure path is incorrectly formatted - // binding infrastructure path pieces are not present in the infrastructure - // inventory + // annotation infrastructure path is incorrectly formatted + // annotation infrastructure path pieces are not present in the + // infrastructure inventory string referential_integrity = 4; // scale up / scale out count @@ -46,6 +46,6 @@ message ValidationResponse { } service InfraService { - // Validate rpc validates both infra and binding messages + // Validate rpc validates both infra and annotation messages rpc Validate(ValidationRequest) returns (ValidationResponse); } \ No newline at end of file diff --git a/keysight_chakra/tests/test_serialization.py b/keysight_chakra/tests/test_serialization.py new file mode 100644 index 0000000..1aad5b5 --- /dev/null +++ b/keysight_chakra/tests/test_serialization.py @@ -0,0 +1,62 @@ +import pytest +from google.protobuf.json_format import MessageToJson, Parse +from google.protobuf.any_pb2 import Any +from google.protobuf.wrappers_pb2 import StringValue +from keysight_chakra.generated.annotate_pb2 import Annotation, Target, Data + + +class AnyString: + """Wrapper class for StringValue and Any""" + + TYPE = "type.googleapis.com/google.protobuf.StringValue" + + @staticmethod + def get_any(value: str) -> Any: + """Returns an Any message with the string encapsulated using StringValue""" + assert isinstance(value, str) + any: Any = Any() + any.Pack(StringValue(value=value)) + return any + + def get_str(any: Any) -> str: + """Returns a str from an Any message""" + assert isinstance(any, Any) + assert any.type_url == AnyString.TYPE + string_value = StringValue() + any.Unpack(string_value) + return string_value.value + + +def test_string_serialization(): + """Test serialization of Any string values in protobuf messages to json + and from json. + """ + annotation_value = "3 Tier Clos Fabric Infrastructure" + + annotation1 = Annotation( + targets=[Target(infrastructure="Infrastructure Description")], + data=Data( + name="Description", + value=AnyString.get_any(annotation_value), + ), + ) + + # serialization to json string + serialized_json = MessageToJson(annotation1) + + # deserialize json string to protobuf message + annotation2 = Annotation() + Parse(serialized_json, annotation2) + + # validate that the original message is the same as the serialized + # to deserialized message + for target1, target2 in zip(annotation1.targets, annotation2.targets): + assert target1.infrastructure == target2.infrastructure + + annotation1_value = AnyString.get_str(annotation1.data.value) + annotation2_value = AnyString.get_str(annotation2.data.value) + assert annotation1_value == annotation2_value == annotation_value + + +if __name__ == "__main__": + pytest.main(["-s", "-o", "log_cli=True", "-o", "log_cli_level=INFO", __file__]) diff --git a/keysight_chakra/validation.py b/keysight_chakra/validation.py index c15341f..60d9f0b 100644 --- a/keysight_chakra/validation.py +++ b/keysight_chakra/validation.py @@ -7,7 +7,7 @@ from google.protobuf.message import Message from generated.service_pb2 import ValidationRequest, ValidationError, ValidationResponse from generated.infra_pb2 import Device -from generated.bind_pb2 import Binding +from generated.annotate_pb2 import Annotation class Validation: @@ -86,7 +86,7 @@ def _validate_device_exists(self, name: str): def _validate_device_connection(self, connection: str): pass - def _validate_binding_infrastructure_path(self, binding: Binding): + def _validate_target_path(self, annotation: Annotation): pass def _validate_count(self, object: Message): @@ -141,8 +141,8 @@ def validate(self, request: ValidationRequest): self._validate_device_exists(device_instance.device) for connection in request.infrastructure.connections: self._validate_device_connection(connection) - if request.bindings is not None: - for binding in request.bindings.bindings: - self._validate_oneof(binding, "infrastructure_path") - self._validate_binding_infrastructure_path(binding) + if request.annotations is not None: + for annotation in request.annotations: + self._validate_oneof(annotation, "infrastructure_path") + self._validate_target_path(annotation) return self._validation_response diff --git a/setup.py b/setup.py index 4a075be..6692294 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ python_requires=">=3.8", author="Keysight", author_email=f"support.ix@keysight.com", - url="https://keysight.com", + url="https://github.com/Keysight/infrastructure", packages=set(package_dir_map.keys()), package_dir=package_dir_map, include_package_data=True,