From fcda9807cfa6a89d691877126b406c5d3909d9b9 Mon Sep 17 00:00:00 2001 From: "serena.spinoso" Date: Thu, 26 Apr 2018 14:19:16 +0200 Subject: [PATCH] Support TOSCA in verigraph (gRPC service) JIRA: PARSER-179 Add TOSCA service description in gRPC server. Add a TOSCA-based client to use the new functionality. Add a JUnit class for testing gRPC service with TOSCA descriptor Change-Id: Id3217a674f076714cd48e3b7e4236e7445d89cd2 Signed-off-by: serena.spinoso --- verigraph/build.xml | 126 +++- verigraph/gRPC-build.xml | 74 +- verigraph/pom.xml | 16 +- verigraph/schema/README.rst | 4 + verigraph/schema/tosca/TOSCA-v1.0.xsd | 790 +++++++++++++++++++++ verigraph/schema/tosca/ToscaTypes_XMLconfig.xsd | 280 ++++++++ .../schema/tosca/ToscaVerigraphDefinitions.xml | 77 ++ verigraph/schema/tosca/yamlToscaDefinitions.yaml | 157 ++++ .../polito/verigraph/grpc/client/ToscaClient.java | 299 ++++++++ .../it/polito/verigraph/grpc/server/GrpcUtils.java | 29 +- .../it/polito/verigraph/grpc/server/Service.java | 169 ++++- .../verigraph/grpc/tosca/test/GrpcToscaTest.java | 425 +++++++++++ .../tosca/converter/grpc/GraphToGrpc.java | 101 +++ .../tosca/converter/grpc/GrpcToGraph.java | 150 ++++ verigraph/src/main/proto/verigraph.proto | 113 ++- 15 files changed, 2743 insertions(+), 67 deletions(-) create mode 100644 verigraph/schema/README.rst create mode 100644 verigraph/schema/tosca/TOSCA-v1.0.xsd create mode 100644 verigraph/schema/tosca/ToscaTypes_XMLconfig.xsd create mode 100644 verigraph/schema/tosca/ToscaVerigraphDefinitions.xml create mode 100644 verigraph/schema/tosca/yamlToscaDefinitions.yaml create mode 100644 verigraph/src/it/polito/verigraph/grpc/client/ToscaClient.java create mode 100644 verigraph/src/it/polito/verigraph/grpc/tosca/test/GrpcToscaTest.java create mode 100644 verigraph/src/it/polito/verigraph/tosca/converter/grpc/GraphToGrpc.java create mode 100644 verigraph/src/it/polito/verigraph/tosca/converter/grpc/GrpcToGraph.java diff --git a/verigraph/build.xml b/verigraph/build.xml index dae4678..ba576a7 100644 --- a/verigraph/build.xml +++ b/verigraph/build.xml @@ -1,6 +1,6 @@ + + + @@ -23,6 +26,9 @@ + + + @@ -86,22 +92,38 @@ - - - - + + + + + + + + - - + - + + + + + + + + + + + + + + - + Building verigraph (if needed)... @@ -166,12 +188,10 @@ - - @@ -181,7 +201,80 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -191,8 +284,6 @@ - - @@ -200,7 +291,12 @@ + + + + + + - \ No newline at end of file diff --git a/verigraph/gRPC-build.xml b/verigraph/gRPC-build.xml index 7ff5549..50483c8 100644 --- a/verigraph/gRPC-build.xml +++ b/verigraph/gRPC-build.xml @@ -1,7 +1,6 @@ + @@ -43,7 +44,7 @@ - + @@ -65,7 +66,7 @@ --> - + Building gRPC (if needed)... - - - - - - - - - - - + Done. @@ -104,20 +105,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -136,6 +138,7 @@ + @@ -151,6 +154,19 @@ + + + Running grpc tests... + + + + + + + + + + @@ -213,4 +229,4 @@ - \ No newline at end of file + diff --git a/verigraph/pom.xml b/verigraph/pom.xml index cfa21e9..9684667 100755 --- a/verigraph/pom.xml +++ b/verigraph/pom.xml @@ -37,8 +37,8 @@ 3.0 true - 1.7 - 1.7 + 1.8 + 1.8 @@ -218,6 +218,16 @@ slf4j-log4j12 1.6.4 + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.9.2 + + + + org.eclipse.persistence + org.eclipse.persistence.core + 2.6.0 + - \ No newline at end of file diff --git a/verigraph/schema/README.rst b/verigraph/schema/README.rst new file mode 100644 index 0000000..c3e7a22 --- /dev/null +++ b/verigraph/schema/README.rst @@ -0,0 +1,4 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +The XML Schema files in this folder are used by VeriGraph to +automatically generate Java XML Bindings (JAXB) diff --git a/verigraph/schema/tosca/TOSCA-v1.0.xsd b/verigraph/schema/tosca/TOSCA-v1.0.xsd new file mode 100644 index 0000000..ebfcaa6 --- /dev/null +++ b/verigraph/schema/tosca/TOSCA-v1.0.xsd @@ -0,0 +1,790 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/verigraph/schema/tosca/ToscaTypes_XMLconfig.xsd b/verigraph/schema/tosca/ToscaTypes_XMLconfig.xsd new file mode 100644 index 0000000..98751b4 --- /dev/null +++ b/verigraph/schema/tosca/ToscaTypes_XMLconfig.xsd @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/verigraph/schema/tosca/ToscaVerigraphDefinitions.xml b/verigraph/schema/tosca/ToscaVerigraphDefinitions.xml new file mode 100644 index 0000000..4f49037 --- /dev/null +++ b/verigraph/schema/tosca/ToscaVerigraphDefinitions.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/verigraph/schema/tosca/yamlToscaDefinitions.yaml b/verigraph/schema/tosca/yamlToscaDefinitions.yaml new file mode 100644 index 0000000..e72a5da --- /dev/null +++ b/verigraph/schema/tosca/yamlToscaDefinitions.yaml @@ -0,0 +1,157 @@ +############################################################################## +# Copyright (c) 2018 Politecnico di Torino and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +#Tosca definitions for Verigraph extension in Tosca Yaml simple profile + +tosca_definitions_version: http://docs.oasis-open.org/tosca/ns/simple/yaml/1.0 + +description: node type definitions exetending tosca types for support to Verigraph verification system + +node_types: + verigraph.types.Antispam: + derived_from: tosca.nodes.Root + description: verigraph Antispam node + properties: + sources: + type: list + entry_schema: + type: string + + verigraph.types.Cache: + derived_from: tosca.nodes.Root + description: verigraph Cache node + properties: + resources: + type: list + entry_schema: + type: string + + verigraph.types.Dpi: + derived_from: tosca.nodes.Root + description: verigraph Dpi node + properties: + notAllowedList: + type: list + entry_schema: + type: string + + verigraph.types.Endhost: + derived_from: tosca.nodes.Root + description: verigraph endhost node + attributes: + body: + type: string + sequence: + type: integer + protocol: + type: string + constraints: + - valid_values: [ HTTP_REQUEST, HTTP_RESPONSE, POP3_REQUEST, POP3_RESOPONSE ] + email_from: + type: string + url: + type: string + options: + type: string + destination: + type: string + + verigraph.types.Endpoint: + derived_from: tosca.nodes.Root + description: verigraph Endpoint node + properties: + names: + type: list + entry_schema: + type: string + + verigraph.types.FieldModifier: + derived_from: tosca.nodes.Root + description: verigraph Field Modifier node + properties: + names: + type: list + entry_schema: + type: string + + verigraph.types.Firewall: + derived_from: tosca.nodes.Root + description: verigraph Firewall node + properties: + elements: + type: map + entry_schema: + description: "source : destination" firewall mapping + type: string + + verigraph.types.MailClient: + derived_from: tosca.nodes.Root + description: verigraph Mail Client node + attributes: + mailserver: + type: string + + verigraph.types.MailServer: + derived_from: tosca.nodes.Root + descrtiption: verigraph Mail Server node + properties: + names: + type: list + entry_schema: + type: string + + verigraph.types.Nat: + derived_from: tosca.nodes.Root + descrtiption: verigraph Nat node + properties: + sources: + type: list + entry_schema: + type: string + + verigraph.types.VpnAccess: + deriver_from: tosca.nodes.Root + descrtiption: verigraph Vpn Access node + attributes: + vpnexit: + type: string + + verigraph.types.VpnExit: + derived_from: tosca.nodes.Root + descrtiption: verigraph Vpn Exit node + attributes: + vpnaccess: + type: string + + verigraph.types.WebClient: + derived_from: tosca.nodes.Root + descrtiption: verigraph web Client node + attributes: + nameWebServer: + type: string + + verigraph.types.WebServer: + derived_from: tosca.nodes.Root + descrtiption: verigraph web Server node + properties: + names: + type: list + entry_schema: + type: string + +relationship_types: + verigraph.relationshipType.generic: + derived_from: tosca.relationships.Root + properties: + name: + type: string + source_id: + type: string + target_id: + type: string diff --git a/verigraph/src/it/polito/verigraph/grpc/client/ToscaClient.java b/verigraph/src/it/polito/verigraph/grpc/client/ToscaClient.java new file mode 100644 index 0000000..dbf8442 --- /dev/null +++ b/verigraph/src/it/polito/verigraph/grpc/client/ToscaClient.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * Copyright (c) 2018 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ +package it.polito.verigraph.grpc.client; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.StatusRuntimeException; +import it.polito.verigraph.grpc.GetRequest; +import it.polito.verigraph.grpc.GraphGrpc; +import it.polito.verigraph.grpc.NewGraph; +import it.polito.verigraph.grpc.NewTopologyTemplate; +import it.polito.verigraph.grpc.NodeTemplateGrpc; +import it.polito.verigraph.grpc.RequestID; +import it.polito.verigraph.grpc.Status; +import it.polito.verigraph.grpc.TopologyTemplateGrpc; +import it.polito.verigraph.grpc.ToscaPolicy; +import it.polito.verigraph.grpc.ToscaRequestID; +import it.polito.verigraph.grpc.ToscaTestGrpc; +import it.polito.verigraph.grpc.ToscaVerificationGrpc; +import it.polito.verigraph.grpc.VerigraphGrpc; + +public class ToscaClient { + + private final ManagedChannel channel; + private final VerigraphGrpc.VerigraphBlockingStub blockingStub; + + public ToscaClient(String host, int port) { + this(ManagedChannelBuilder.forAddress(host, port).usePlaintext(true)); + } + + /** Construct client for accessing toscaVerigraph server using the existing channel. */ + public ToscaClient(ManagedChannelBuilder channelBuilder) { + channel = channelBuilder.build(); + blockingStub = VerigraphGrpc.newBlockingStub(channel); + } + + /** Close the channel */ + public void shutdown() throws InterruptedException { + channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); + } + + /** Obtain a list of the available TopologyTemplates*/ + public List getTopologyTemplates(){ + List templates = new ArrayList(); + GetRequest request = GetRequest.newBuilder().build(); + boolean response_ok = true; + + /*Iterates on received topology templates, prints on log file in case of errors*/ + Iterator receivedTemplates; + try { + receivedTemplates = blockingStub.getTopologyTemplates(request); + System.out.println("++ Receiving TopologyTemplates..."); + while(receivedTemplates.hasNext()) { + TopologyTemplateGrpc received = receivedTemplates.next(); + if(received.getErrorMessage().equals("")) { + System.out.println("++ Correctly received TopologyTemplate --> id:" + received.getId()); + templates.add(received); + } else + System.out.println("-- Received a TopologyTemplate with error: " + received.getErrorMessage()); + } + } catch (StatusRuntimeException ex) { + System.out.println("-- RPC failed: " + ex.getMessage()); + response_ok = false; + } + if(response_ok) { + System.out.println("++ All TopologyTemplates correctly received."); + return templates; + } else { + return null; //Function returns null in case of error to differentiate from empty list case. + } + } + + + /** Obtain a TopologyTemplate by ID */ + public TopologyTemplateGrpc getTopologyTemplate(String id) { + ToscaRequestID request = ToscaRequestID.newBuilder().setIdTopologyTemplate(id).build(); + TopologyTemplateGrpc response = TopologyTemplateGrpc.newBuilder().build(); + try { + System.out.println("++ Receiving TopologyTemplate..."); + response = blockingStub.getTopologyTemplate(request); + if(response.getErrorMessage().equals("")){ + System.out.println("++ Received TopologyTemplate --> id:" + response.getId()); + return response; + } else { + System.out.println("-- Error: " + response.getErrorMessage()); + return response; + } + } catch (StatusRuntimeException ex) { + System.out.println("-- RPC failed: " + ex.getStatus()); + return TopologyTemplateGrpc.newBuilder().setErrorMessage(ex.getStatus().getDescription()).build(); + } + } + + + /** Creates a new TopologyTemplate, takes in input a TopologyTemplateGrpc */ + public NewTopologyTemplate createTopologyTemplate(TopologyTemplateGrpc topol) { + try { + //Sending new Topology and analyzing response + System.out.println("++ Sending the new TopologyTemplate..."); + NewTopologyTemplate response = blockingStub.createTopologyTemplate(topol); + if(response.getSuccess()) + System.out.println("++ TopologyTemplate successfully created with id: "+ response.getTopologyTemplate().getId()); + else + System.out.println("-- TopologyTemplate creation failed: " + response.getErrorMessage()); + return response; + + } catch (StatusRuntimeException ex) { + System.out.println("-- RPC failed: " + ex.getStatus()); + return NewTopologyTemplate.newBuilder().setSuccess(false).setErrorMessage(ex.getStatus().getDescription()).build(); + //getDescription may be empty + } + } + + + /** Update a TopologyTemplate, takes in input a TopologyTemplateGrpc and the Topology's ID to be updated*/ + public NewTopologyTemplate updateTopologyTemplate(TopologyTemplateGrpc topol, String id) { + //Checking if the inserted string is an object + try { + Long.valueOf(id); + } catch (NumberFormatException ex) { + System.out.println("-- The ID must a number according to Verigraph implementation."); + return NewTopologyTemplate.newBuilder().setSuccess(false) + .setErrorMessage("The ID must a number according to Verigraph implementation.").build(); + } + + //Update the topology ID + TopologyTemplateGrpc.Builder updTopol = TopologyTemplateGrpc.newBuilder(); + try { + updTopol.setId(id) + .addAllNodeTemplate(topol.getNodeTemplateList()) + .addAllRelationshipTemplate(topol.getRelationshipTemplateList()); + } catch (Exception ex) { + System.out.println("-- Error: Incorrect fields implementation."); + return NewTopologyTemplate.newBuilder().setSuccess(false).setErrorMessage("Error: Incorrect fields implementation.").build(); + } + + //Sending updated Topology and analyzing response + try { + + System.out.println("++ Sending the updated TopologyTemplate..."); + NewTopologyTemplate response = blockingStub.updateTopologyTemplate(updTopol.build()); + if(response.getSuccess()) + System.out.println("++ TopologyTemplate successfully updated."); + else + System.out.println("-- TopologyTemplate not updated: " + response.getErrorMessage()); + return response; + + } catch (StatusRuntimeException ex) { + System.out.println("-- RPC failed: " + ex.getStatus()); + return NewTopologyTemplate.newBuilder().setSuccess(false).setErrorMessage(ex.getStatus().getDescription()).build(); + } + } + + + /** Delete a TopologyTemplate by ID */ + public Status deleteTopologyTemplate(String id) { + try { + Long.valueOf(id); + } catch (NumberFormatException ex) { + System.out.println("-- The ID must a number according to Verigraph implementation."); + return Status.newBuilder().setSuccess(false) + .setErrorMessage("The ID must a number according to Verigraph implementation.").build(); + } + ToscaRequestID request = ToscaRequestID.newBuilder().setIdTopologyTemplate(id).build(); + try { + System.out.println("++ Sending delete request..."); + Status response = blockingStub.deleteTopologyTemplate(request); + if(response.getSuccess()) + System.out.println("++ TopologyTemplate successfully deleted."); + else + System.out.println("-- Error deleting TopologyTemplate : " + response.getErrorMessage()); + return response; + + } catch (StatusRuntimeException ex) { + System.out.println("-- RPC failed: " + ex.getStatus()); + return Status.newBuilder().setSuccess(false).setErrorMessage(ex.getStatus().getDescription()).build(); + } + } + + + /** VerifyPolicy */ + public ToscaVerificationGrpc verifyPolicy(ToscaPolicy policy){ + ToscaVerificationGrpc response; + try { + System.out.println("++ Sending ToscaPolicy..."); + response = blockingStub.verifyToscaPolicy(policy); + if(!response.getErrorMessage().equals("")){ + System.out.println("-- Error in operation: " + response.getErrorMessage()); + } + else { + System.out.println("++ Result: " + response.getResult()); + System.out.println("++ Comment: " + response.getComment()); + + for(ToscaTestGrpc test : response.getTestList()){ + System.out.println("++ Traversed nodes:"); + for(NodeTemplateGrpc node : test.getNodeTemplateList()){ + System.out.println("\t Node "+node.getName()); + } + } + } + return response; + } catch (StatusRuntimeException e) { + System.out.println("-- RPC failed: " + e.getStatus()); + return ToscaVerificationGrpc.newBuilder().setSuccessOfOperation(false) + .setErrorMessage(e.getStackTrace().toString()).build(); + } + } + + + //Methods added for backward compatibility with JSON grpc, only create and update methods need to be redefined + //The reason is that the tosca Grpc converter requires a coherent numbering of IDs while the previous + //implementations exploits names to identify nodes and can considers IDs as not strictly required attributes. + + public NewGraph createGraph(GraphGrpc gr) { + NewGraph response; + try { + response = blockingStub.createGraph(gr); + if(response.getSuccess()) + System.out.println("++ TopologyTemplate successfully created with id: "+ response.getGraph().getId()); + else + System.out.println("-- TopologyTemplate creation failed: " + response.getErrorMessage()); + return response; + } catch (StatusRuntimeException e) { + System.err.println("-- RPC failed: " + e.getStatus()); + return NewGraph.newBuilder().setSuccess(false).setErrorMessage(e.getStatus().getDescription()).build(); + } + + } + + public NewGraph updateGraph(long idGraph, GraphGrpc newGraph) { + + GraphGrpc gr = GraphGrpc.newBuilder(newGraph).setId(idGraph).build(); + NewGraph response; + try { + response = blockingStub.updateGraph(gr); + if(response.getSuccess()) + System.out.println("++ TopologyTemplate successfully created with id: "+ response.getGraph().getId()); + else + System.out.println("-- TopologyTemplate creation failed: " + response.getErrorMessage()); + return response; + } catch (StatusRuntimeException e) { + System.err.println("-- RPC failed: " + e.getStatus()); + return NewGraph.newBuilder().setSuccess(false).setErrorMessage(e.getStatus().getDescription()).build(); + } + } + + public GraphGrpc getGraph(long idGraph) { + + RequestID request = RequestID.newBuilder().setIdGraph(idGraph).build() ; + try { + System.out.println("++ Receiving TopologyTemplate..."); + GraphGrpc graph = blockingStub.getGraph(request); + System.out.println("++ Received TopologyTemplate --> id:" + graph.getId()); + if(!graph.getErrorMessage().equals("")){ + System.out.println("-- Error : " + graph.getErrorMessage()); + return graph; + } + return graph; + } catch (StatusRuntimeException ex) { + System.err.println("-- RPC failed: " + ex.getStatus()); + return null; + } + } + + + public List getGraphs() { + List graphsReceived = new ArrayList(); + GetRequest request = GetRequest.newBuilder().build(); + Iterator graphs; + try { + graphs = blockingStub.getGraphs(request); + System.out.println("++ Receiving TopologyTemplates..."); + while (graphs.hasNext()) { + GraphGrpc graph = graphs.next(); + if(graph.getErrorMessage().equals("")){ + System.out.println("++ Correctly received graph --> id:" + graph.getId()); + graphsReceived.add(graph); + }else{ + System.out.println("-- Received a graph with error : " + graph.getErrorMessage()); + } + } + } catch (StatusRuntimeException ex) { + System.err.println("-- RPC failed : " + ex.getStatus()); + return null; + } + return graphsReceived; + } +} diff --git a/verigraph/src/it/polito/verigraph/grpc/server/GrpcUtils.java b/verigraph/src/it/polito/verigraph/grpc/server/GrpcUtils.java index 43859db..96c52a5 100644 --- a/verigraph/src/it/polito/verigraph/grpc/server/GrpcUtils.java +++ b/verigraph/src/it/polito/verigraph/grpc/server/GrpcUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Politecnico di Torino and others. + * Copyright (c) 2017/18 Politecnico di Torino and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License, Version 2.0 @@ -12,16 +12,18 @@ import java.io.IOException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Splitter; + import it.polito.verigraph.grpc.ConfigurationGrpc; import it.polito.verigraph.grpc.GraphGrpc; import it.polito.verigraph.grpc.NeighbourGrpc; import it.polito.verigraph.grpc.NodeGrpc; +import it.polito.verigraph.grpc.NodeGrpc.FunctionalType; import it.polito.verigraph.grpc.TestGrpc; import it.polito.verigraph.grpc.VerificationGrpc; -import it.polito.verigraph.grpc.NodeGrpc.FunctionalType; import it.polito.verigraph.model.Configuration; import it.polito.verigraph.model.Graph; import it.polito.verigraph.model.Neighbour; @@ -29,6 +31,7 @@ import it.polito.verigraph.model.Node; import it.polito.verigraph.model.Test; import it.polito.verigraph.model.Verification; + public class GrpcUtils { private static final Logger logger = Logger.getLogger(GrpcUtils.class.getName()); @@ -43,6 +46,7 @@ public class GrpcUtils { //id is not present Neighbour ne = new Neighbour(); ne.setName(request.getName()); + ne.setId(request.getId()); return ne; } @@ -86,13 +90,17 @@ public class GrpcUtils { } public static Node deriveNode(NodeGrpc request) { - //id is not present + //id is not present Node node = new Node(); node.setName(request.getName()); node.setFunctional_type(request.getFunctionalType().toString()); Configuration conf = deriveConfiguration(request.getConfiguration()); node.setConfiguration(conf); + //Modification for Tosca CLI + Long id = request.getId(); + if( id != null) node.setId(request.getId()); + Map neighours = node.getNeighbours(); long i = 1; for(NeighbourGrpc neighbour:request.getNeighbourList()){ @@ -106,7 +114,7 @@ public class GrpcUtils { public static GraphGrpc obtainGraph(Graph graph){ GraphGrpc.Builder gr = GraphGrpc.newBuilder(); gr.setId(graph.getId()); - for(Node node:graph.getNodes().values()){ + for(Node node : graph.getNodes().values()){ NodeGrpc ng = obtainNode(node); gr.addNode(ng); } @@ -116,14 +124,16 @@ public class GrpcUtils { public static Graph deriveGraph(GraphGrpc request) { //id is not present Graph graph = new Graph(); - + //Modification for Tosca CLI + Long id = request.getId(); + if( id != null) graph.setId(request.getId()); + long i=1; Map nodes= graph.getNodes(); for(NodeGrpc node:request.getNodeList()){ Node ng = deriveNode(node); - nodes.put(i++, ng); + nodes.put(i++, ng); } - return graph; } @@ -142,12 +152,13 @@ public class GrpcUtils { return ver.build(); } - /**Intended for string that begins with "?" - * */ + + /** Intended for string that begins with "?" */ public static Map getParamGivenString(String str){ String string = str.substring(1); final Map map = Splitter.on('&').trimResults().withKeyValueSeparator("="). split(string); return map; } + } diff --git a/verigraph/src/it/polito/verigraph/grpc/server/Service.java b/verigraph/src/it/polito/verigraph/grpc/server/Service.java index 1839b7e..c6b69b0 100644 --- a/verigraph/src/it/polito/verigraph/grpc/server/Service.java +++ b/verigraph/src/it/polito/verigraph/grpc/server/Service.java @@ -15,9 +15,13 @@ import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; + import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; +import it.polito.verigraph.exception.BadRequestException; +import it.polito.verigraph.exception.DataNotFoundException; +import it.polito.verigraph.exception.ForbiddenException; import it.polito.verigraph.grpc.ConfigurationGrpc; import it.polito.verigraph.grpc.GetRequest; import it.polito.verigraph.grpc.GraphGrpc; @@ -25,15 +29,17 @@ import it.polito.verigraph.grpc.NeighbourGrpc; import it.polito.verigraph.grpc.NewGraph; import it.polito.verigraph.grpc.NewNeighbour; import it.polito.verigraph.grpc.NewNode; +import it.polito.verigraph.grpc.NewTopologyTemplate; import it.polito.verigraph.grpc.NodeGrpc; import it.polito.verigraph.grpc.Policy; import it.polito.verigraph.grpc.RequestID; import it.polito.verigraph.grpc.Status; +import it.polito.verigraph.grpc.TopologyTemplateGrpc; +import it.polito.verigraph.grpc.ToscaPolicy; +import it.polito.verigraph.grpc.ToscaRequestID; +import it.polito.verigraph.grpc.ToscaVerificationGrpc; import it.polito.verigraph.grpc.VerificationGrpc; import it.polito.verigraph.grpc.VerigraphGrpc; -import it.polito.verigraph.exception.BadRequestException; -import it.polito.verigraph.exception.DataNotFoundException; -import it.polito.verigraph.exception.ForbiddenException; import it.polito.verigraph.model.Configuration; import it.polito.verigraph.model.Graph; import it.polito.verigraph.model.Neighbour; @@ -44,6 +50,8 @@ import it.polito.verigraph.service.GraphService; import it.polito.verigraph.service.NeighbourService; import it.polito.verigraph.service.NodeService; import it.polito.verigraph.service.VerificationService; +import it.polito.verigraph.tosca.converter.grpc.GraphToGrpc; +import it.polito.verigraph.tosca.converter.grpc.GrpcToGraph; public class Service { /** Port on which the server should run. */ @@ -107,7 +115,7 @@ public class Service { } } - /**Here start method of my implementation*/ + /** Here start methods */ private class VerigraphImpl extends VerigraphGrpc.VerigraphImplBase{ /** Here start methods of GraphResource*/ @@ -307,8 +315,7 @@ public class Service { @Override public void updateNode(NodeGrpc request, StreamObserver responseObserver) { - NewNode.Builder response = NewNode.newBuilder(); - try{ + NewNode.Builder response = NewNode.newBuilder(); try{ Node node = GrpcUtils.deriveNode(request); node.setId(request.getId()); Node newNode = nodeService.updateNode(request.getIdGraph(), node); @@ -340,7 +347,8 @@ public class Service { } Node node = nodeService.getNode(request.getIdGraph(), request.getIdNode()); if (node == null){ - throw new BadRequestException("Node with id " + request.getIdNode() + " not found in graph with id " + request.getIdGraph()); + throw new BadRequestException("Node with id " + request.getIdNode() + + " not found in graph with id " + request.getIdGraph()); } Configuration nodeConfiguration = GrpcUtils.deriveConfiguration(request); Node nodeCopy = new Node(); @@ -386,8 +394,7 @@ public class Service { NeighbourGrpc nr = NeighbourGrpc.newBuilder().setErrorMessage(internalError).build(); responseObserver.onNext(nr); logger.log(Level.WARNING, ex.getMessage()); - } - responseObserver.onCompleted(); + } responseObserver.onCompleted(); } @Override @@ -462,5 +469,149 @@ public class Service { responseObserver.onNext(response.build()); responseObserver.onCompleted(); } + + /** Here start methods of TOSCA gRPC server */ @Override + public void getTopologyTemplates (GetRequest request, StreamObserver responseObserver) { + boolean not_correct = false; + try { + for(Graph item : graphService.getAllGraphs()) { + TopologyTemplateGrpc topol = GraphToGrpc.obtainTopologyTemplate(item); + responseObserver.onNext(topol); + } + } catch(Exception ex){ + logger.log(Level.WARNING, ex.getMessage()); + not_correct = true; + } + if(not_correct) + responseObserver.onNext(TopologyTemplateGrpc.newBuilder() + .setErrorMessage("Internal Server Error while retrieving TopologyTemplate").build()); + responseObserver.onCompleted(); + } + + @Override + public void getTopologyTemplate (ToscaRequestID request, StreamObserver responseObserver) { + try { + Long graphID = Long.valueOf(request.getIdTopologyTemplate()); + //this method will throw a NumberFormatException in case the ID is not representable as a long + Graph graph = graphService.getGraph(graphID); + TopologyTemplateGrpc topol = GraphToGrpc.obtainTopologyTemplate(graph); + responseObserver.onNext(topol); + } catch(ForbiddenException | DataNotFoundException ex) { + TopologyTemplateGrpc topolError = TopologyTemplateGrpc.newBuilder().setErrorMessage(ex.getMessage()).build(); + responseObserver.onNext(topolError); + logger.log(Level.WARNING, ex.getMessage()); + } catch(NumberFormatException ex) { + TopologyTemplateGrpc topolError = TopologyTemplateGrpc.newBuilder() + .setErrorMessage("The TopologyTemplate ID must be a long value.").build(); + responseObserver.onNext(topolError); + logger.log(Level.WARNING, ex.getMessage()); + } catch(Exception ex) { + TopologyTemplateGrpc topolError = TopologyTemplateGrpc.newBuilder().setErrorMessage(internalError).build(); + responseObserver.onNext(topolError); + logger.log(Level.WARNING, ex.getMessage()); + } + responseObserver.onCompleted(); + } + + @Override + public void createTopologyTemplate (TopologyTemplateGrpc request, StreamObserver responseObserver) { + NewTopologyTemplate.Builder response = NewTopologyTemplate.newBuilder(); + try{ + Graph graph = GrpcToGraph.deriveGraph(request); + Graph newGraph = graphService.addGraph(graph); + response.setSuccess(true).setTopologyTemplate(GraphToGrpc.obtainTopologyTemplate(newGraph)); + } catch(BadRequestException ex) { + ex.printStackTrace(); + response.setSuccess(false).setErrorMessage("Provided invalid request to the service."); + logger.log(Level.WARNING, ex.getClass().toString()); + logger.log(Level.WARNING, ex.getMessage()); + } catch(Exception ex) { + ex.printStackTrace(); + response.setSuccess(false).setErrorMessage(internalError); + logger.log(Level.WARNING, ex.getClass().toString()); + logger.log(Level.WARNING, ex.getMessage()); + } + responseObserver.onNext(response.build()); + responseObserver.onCompleted(); + } + + + @Override + public void updateTopologyTemplate (TopologyTemplateGrpc request, StreamObserver responseObserver) { + NewTopologyTemplate.Builder response = NewTopologyTemplate.newBuilder(); + try{ + Graph graph = GrpcToGraph.deriveGraph(request); + Graph newGraph = graphService.updateGraph(graph); + response.setSuccess(true).setTopologyTemplate(GraphToGrpc.obtainTopologyTemplate(newGraph)); + } catch(ForbiddenException | DataNotFoundException | BadRequestException ex){ + response.setSuccess(false).setErrorMessage(ex.getMessage()); + logger.log(Level.WARNING, ex.getMessage()); + } catch(Exception ex){ + response.setSuccess(false).setErrorMessage(internalError); + logger.log(Level.WARNING, ex.getMessage()); + } + responseObserver.onNext(response.build()); + responseObserver.onCompleted(); + } + + + @Override + public void deleteTopologyTemplate (ToscaRequestID request, StreamObserver responseObserver) { + Status.Builder response = Status.newBuilder(); + Long graphID = null; + try{ + graphID = Long.valueOf(request.getIdTopologyTemplate()); + //this method will throw a NumberFormatException in case the ID is not representable as a long + graphService.removeGraph(graphID); + response.setSuccess(true); + } catch(ForbiddenException | DataNotFoundException ex) { + response.setSuccess(false).setErrorMessage(ex.getMessage()); + logger.log(Level.WARNING, ex.getMessage()); + } catch(NumberFormatException ex) { + response.setSuccess(false).setErrorMessage("The TopologyTemplate ID must be a long value."); + logger.log(Level.WARNING, ex.getMessage()); + } catch(Exception ex) { + response.setSuccess(false).setErrorMessage(internalError); + logger.log(Level.WARNING, ex.getMessage()); + } + responseObserver.onNext(response.build()); + responseObserver.onCompleted(); + } + + + @Override + public void verifyToscaPolicy(ToscaPolicy request, StreamObserver responseObserver) { + try{ + //Convert request + VerificationBean verify = new VerificationBean(); + verify.setDestination(request.getDestination()); + verify.setSource(request.getSource()); + verify.setType(request.getType().toString()); + verify.setMiddlebox(request.getMiddlebox()); + + //Convert Response + Long graphID = Long.valueOf(request.getIdTopologyTemplate()); + //this method will throw a NumberFormatException in case the ID is not representable as a long + Verification ver = verificationService.verify(graphID, verify); + responseObserver.onNext(GraphToGrpc.obtainToscaVerification(ver)); + } catch(ForbiddenException | DataNotFoundException | BadRequestException ex) { + ToscaVerificationGrpc verError = ToscaVerificationGrpc.newBuilder().setSuccessOfOperation(false) + .setErrorMessage(ex.getMessage()).build(); + responseObserver.onNext(verError); + logger.log(Level.WARNING, ex.getMessage()); + } catch(NumberFormatException ex) { + ToscaVerificationGrpc verError = ToscaVerificationGrpc.newBuilder().setSuccessOfOperation(false) + .setErrorMessage("The TopologyTemplate ID must be a long value.").build(); + responseObserver.onNext(verError); + logger.log(Level.WARNING, ex.getMessage()); + } catch(Exception ex) { + ToscaVerificationGrpc verError = ToscaVerificationGrpc.newBuilder().setSuccessOfOperation(false) + .setErrorMessage(internalError).build(); + responseObserver.onNext(verError); + logger.log(Level.WARNING, ex.getMessage()); + } responseObserver.onCompleted(); + } + + } } diff --git a/verigraph/src/it/polito/verigraph/grpc/tosca/test/GrpcToscaTest.java b/verigraph/src/it/polito/verigraph/grpc/tosca/test/GrpcToscaTest.java new file mode 100644 index 0000000..5771406 --- /dev/null +++ b/verigraph/src/it/polito/verigraph/grpc/tosca/test/GrpcToscaTest.java @@ -0,0 +1,425 @@ +/******************************************************************************* + * Copyright (c) 2018 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ +package it.polito.verigraph.grpc.tosca.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.MethodSorters; + +import it.polito.verigraph.grpc.NewTopologyTemplate; +import it.polito.verigraph.grpc.NodeTemplateGrpc; +import it.polito.verigraph.grpc.NodeTemplateGrpc.Type; +import it.polito.verigraph.grpc.RelationshipTemplateGrpc; +import it.polito.verigraph.grpc.Status; +import it.polito.verigraph.grpc.TopologyTemplateGrpc; +import it.polito.verigraph.grpc.ToscaConfigurationGrpc; +import it.polito.verigraph.grpc.ToscaPolicy; +import it.polito.verigraph.grpc.ToscaPolicy.PolicyType; +import it.polito.verigraph.grpc.ToscaVerificationGrpc; +import it.polito.verigraph.grpc.client.ToscaClient; +import it.polito.verigraph.grpc.server.Service; + +@RunWith(JUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class GrpcToscaTest { + private Service server; + private ToscaClient client; + private TopologyTemplateGrpc testTemplate, simpleTestTemplate; + + public GrpcToscaTest() { + this.generateTestTemplate(); + } + + @Before + public void setUpBeforeClass() throws Exception { + client = new ToscaClient("localhost" , 50051); + server = new Service(50051); + server.start(); + } + + @After + public void tearDown() throws Exception { + server.stop(); + client.shutdown(); + } + + + @Test + public void test0Creation() { + System.out.println("\nTest A: Graph Creation."); + + NewTopologyTemplate response = client.createTopologyTemplate(testTemplate); + assertNotNull("Returned a NULL graph", response); + assertEquals(response.getSuccess(), true); + assertEquals("Error report: " + response.getErrorMessage(), "", response.getErrorMessage()); + + Status resp = client.deleteTopologyTemplate(response.getTopologyTemplate().getId()); + assertEquals("Error while deleting testTemplate.", true, resp.getSuccess()); + + System.out.println("Test A completed\n"); + + return; + } + + + @Test + public void test1Reading() { + System.out.println("\nTest B: Graph Reading."); + + //Creating a test graph on remote repository + System.out.println("Phase B.1 -- Creating a test graph."); + NewTopologyTemplate response = client.createTopologyTemplate(simpleTestTemplate); + assertNotNull("Returned a NULL graph", response); + assertEquals(true, response.getSuccess()); + assertEquals("Error report: " + response.getErrorMessage(), "", response.getErrorMessage()); + + //Reading remote graph. + System.out.println("Phase B.2 -- Reading remote graph."); + TopologyTemplateGrpc retrieved = client.getTopologyTemplate(response.getTopologyTemplate().getId()); + assertNotNull("Retrieved a NULL graph", retrieved); + assertEquals(retrieved.getId(), response.getTopologyTemplate().getId()); + + //Nodes checking + System.out.println("Phase B.3 -- Checking graph's nodes."); + assertEquals(retrieved.getNodeTemplateCount(), 3); + assertEquals("Node1 name error", response.getTopologyTemplate().getNodeTemplateList().get(0).getName(), + retrieved.getNodeTemplateList().get(0).getName()); + assertEquals("Node2 name error", response.getTopologyTemplate().getNodeTemplateList().get(1).getName(), + retrieved.getNodeTemplateList().get(1).getName()); + assertEquals("Node3 name error", response.getTopologyTemplate().getNodeTemplateList().get(2).getName(), + retrieved.getNodeTemplateList().get(2).getName()); + + //Relationships checking + System.out.println("Phase B.4 -- Checking graph's relationships."); + assertEquals(retrieved.getRelationshipTemplateCount(), 4); + String source1=null, target1=null; + String source2=null, target2=null; + String source3=null, target3=null; + String source4=null, target4=null; + for (NodeTemplateGrpc node : retrieved.getNodeTemplateList()){ + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(0).getIdSourceNodeTemplate())) + source1=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(0).getIdTargetNodeTemplate())) + target1=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(1).getIdSourceNodeTemplate())) + source2=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(1).getIdTargetNodeTemplate())) + target2=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(2).getIdSourceNodeTemplate())) + source3=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(2).getIdTargetNodeTemplate())) + target3=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(3).getIdSourceNodeTemplate())) + source4=node.getName(); + if(node.getId().equals(retrieved.getRelationshipTemplateList().get(3).getIdTargetNodeTemplate())) + target4=node.getName(); + } + + assertEquals("Relat1 name error", retrieved.getRelationshipTemplateList().get(0).getName(), source1+"to"+target1); + assertEquals("Relat2 name error", retrieved.getRelationshipTemplateList().get(1).getName(), source2+"to"+target2); + assertEquals("Relat3 name error", retrieved.getRelationshipTemplateList().get(2).getName(), source3+"to"+target3); + assertEquals("Relat4 name error", retrieved.getRelationshipTemplateList().get(3).getName(), source4+"to"+target4); + + System.out.println("Phase B.5 -- Deleting graph."); + Status resp = client.deleteTopologyTemplate(response.getTopologyTemplate().getId()); + assertEquals("Error while deleting simpleTestTemplate", true, resp.getSuccess()); + + + System.out.println("Test B completed.\n"); + return; + } + + @Test + public void test2Update() { + System.out.println("\nTest C: Update."); + + //Creating a test graph on remote repository + System.out.println("Phase C.1 -- Creating a test graph."); + NewTopologyTemplate response = client.createTopologyTemplate(simpleTestTemplate); + assertNotNull("Returned a NULL graph", response); + assertEquals(true, response.getSuccess()); + assertEquals("Error report: " + response.getErrorMessage(), "", response.getErrorMessage()); + + //Reading remote graph. + System.out.println("Phase C.2 -- Reading remote graph."); + TopologyTemplateGrpc retrieved = client.getTopologyTemplate(response.getTopologyTemplate().getId()); + assertNotNull("Retrieved a NULL graph", retrieved); + assertEquals(retrieved.getId(), response.getTopologyTemplate().getId()); + + //Updating a TopologyTemplateGrpc + System.out.println("Phase C.3 -- Updating a test graph."); + TopologyTemplateGrpc.Builder templ = TopologyTemplateGrpc.newBuilder(); + List nodes = new ArrayList(); + List relats = new ArrayList(); + + ToscaConfigurationGrpc node1conf = ToscaConfigurationGrpc.newBuilder().setDescription("node1configuration") + .setId("15").setConfiguration("[]").build(); + NodeTemplateGrpc node1 = NodeTemplateGrpc.newBuilder().setConfiguration(node1conf).setId("999") + .setName("webserver1").setType(Type.webserver).build(); + nodes.add(node1); + + ToscaConfigurationGrpc node2conf = ToscaConfigurationGrpc.newBuilder().setDescription("node2configuration") + .setId("16").setConfiguration("[{\r\n\"protocol\":\"HTTP_REQUEST\",\r\n \"url\":\"www.facebook.com\"\r\n }]").build(); + NodeTemplateGrpc node2 = NodeTemplateGrpc.newBuilder().setConfiguration(node2conf).setId("888") + .setName("host2").setType(Type.endhost).build(); + nodes.add(node2); + + RelationshipTemplateGrpc rel0 = RelationshipTemplateGrpc.newBuilder().setId("1001") + .setIdSourceNodeTemplate("999").setIdTargetNodeTemplate("888").setName("webserver1tohost2").build(); + relats.add(rel0); + + RelationshipTemplateGrpc rel1 = RelationshipTemplateGrpc.newBuilder().setId("1002") + .setIdSourceNodeTemplate("888").setIdTargetNodeTemplate("999").setName("host2towebserver1").build(); + relats.add(rel1); + + TopologyTemplateGrpc newTestTemplate = templ.addAllNodeTemplate(nodes).addAllRelationshipTemplate(relats).setId("9").build(); + NewTopologyTemplate updated = client.updateTopologyTemplate(newTestTemplate, response.getTopologyTemplate().getId()); + assertNotNull("Returned a NULL graph", updated); + assertEquals(true, updated.getSuccess()); + assertEquals("Error report: " + updated.getErrorMessage(), "", updated.getErrorMessage()); + + //Reading remote graph. + System.out.println("Phase C.4 -- Reading remote graph."); + TopologyTemplateGrpc retrieved2 = client.getTopologyTemplate(response.getTopologyTemplate().getId()); + assertNotNull("Retrieved a NULL graph", retrieved2); + assertEquals(retrieved2.getId(), response.getTopologyTemplate().getId()); + + //Nodes checking + System.out.println("Phase C.5 -- Checking updated graph's nodes."); + assertEquals(retrieved2.getNodeTemplateCount(), 2); + assertEquals("Node1 name error", updated.getTopologyTemplate().getNodeTemplateList().get(0).getName(), + retrieved2.getNodeTemplateList().get(0).getName()); + assertEquals("Node2 name error", updated.getTopologyTemplate().getNodeTemplateList().get(1).getName(), + retrieved2.getNodeTemplateList().get(1).getName()); + + //Relationships checking + System.out.println("Phase C.6 -- Checking updated graph's relationships."); + assertEquals(retrieved2.getRelationshipTemplateCount(), 2); + String source1=null, target1=null; + String source2=null, target2=null; + for (NodeTemplateGrpc node : retrieved2.getNodeTemplateList()){ + if(node.getId().equals(retrieved2.getRelationshipTemplateList().get(0).getIdSourceNodeTemplate())) + source1=node.getName(); + if(node.getId().equals(retrieved2.getRelationshipTemplateList().get(0).getIdTargetNodeTemplate())) + target1=node.getName(); + if(node.getId().equals(retrieved2.getRelationshipTemplateList().get(1).getIdSourceNodeTemplate())) + source2=node.getName(); + if(node.getId().equals(retrieved2.getRelationshipTemplateList().get(1).getIdTargetNodeTemplate())) + target2=node.getName(); + } + + assertEquals("Relat1 name error", retrieved2.getRelationshipTemplateList().get(0).getName(), source1+"to"+target1); + assertEquals("Relat2 name error", retrieved2.getRelationshipTemplateList().get(1).getName(), source2+"to"+target2); + + System.out.println("Phase C.6 -- Deleting graph."); + Status resp = client.deleteTopologyTemplate(updated.getTopologyTemplate().getId()); + assertEquals("Error while deleting simpleTestTemplate", true, resp.getSuccess()); + + System.out.println("Test C completed.\n"); + return; + } + + + @Test + public void test3Verification() { + System.out.println("\nTest D: Verification."); + NewTopologyTemplate response = client.createTopologyTemplate(testTemplate); + if(response == null | response.getSuccess() != true) { + fail("Test failed, unable to load the graph."); + return; + } + + //The Id of the graph on which we are going to perform tests + String testTemplateId = response.getTopologyTemplate().getId(); + + //REACHABILITY test + System.out.println("Phase 1.1 - Reachability SAT."); + ToscaPolicy policy = ToscaPolicy.newBuilder().setIdTopologyTemplate(testTemplateId) + .setType(PolicyType.reachability).setSource("host2").setDestination("host1").build(); + ToscaVerificationGrpc result = client.verifyPolicy(policy); + assertNotNull("There was no response", result); + assertEquals("Unexpected result : " + result.getResult() + " - " + result.getComment(), "SAT", result.getResult()); + assertEquals("Error report: " + result.getErrorMessage(), "", result.getErrorMessage()); + + result = null; + System.out.println("Phase 1.2 - Reachability UNSAT."); + policy = ToscaPolicy.newBuilder().setIdTopologyTemplate(testTemplateId) + .setType(PolicyType.reachability).setSource("host1").setDestination("antispamNode1").build(); + result = client.verifyPolicy(policy); + assertNotNull("There was no response", result); + assertEquals("Unexpected result : " + result.getResult() + " - " + result.getComment(), "UNSAT", result.getResult()); + assertEquals("Error report: " + result.getErrorMessage(), "", result.getErrorMessage()); + + //ISOLATION test + result = null; + System.out.println("Phase 2.1 - Isolation SAT."); + policy = ToscaPolicy.newBuilder().setIdTopologyTemplate(testTemplateId) + .setType(PolicyType.isolation).setSource("host2").setDestination("host1").setMiddlebox("webserver1").build(); + result = client.verifyPolicy(policy); + assertNotNull("There was no response", result); + assertEquals("Unexpected result : " + result.getResult() + " - " + result.getComment(), "SAT", result.getResult()); + assertEquals("Error report: " + result.getErrorMessage(), "", result.getErrorMessage()); + + System.out.println("Phase 2.2 - Isolation UNSAT."); + policy = ToscaPolicy.newBuilder().setIdTopologyTemplate(testTemplateId) + .setType(PolicyType.isolation).setSource("host2").setDestination("host1").setMiddlebox("fw").build(); + result = client.verifyPolicy(policy); + assertNotNull("There was no response", result); + assertEquals("Unexpected result : " + result.getResult() + " - " + result.getComment(), "UNSAT", result.getResult()); + assertEquals("Error report: " + result.getErrorMessage(), "", result.getErrorMessage()); + + //TRAVERSAL test + result = null; + System.out.println("Phase 3.1 - Traversal SAT."); + policy = ToscaPolicy.newBuilder().setIdTopologyTemplate(testTemplateId) + .setType(PolicyType.traversal).setSource("host2").setDestination("host1").setMiddlebox("fw").build(); + result = client.verifyPolicy(policy); + assertNotNull("There was no response", result); + assertEquals("Unexpected result : " + result.getResult() + " - " + result.getComment(), "SAT", result.getResult()); + assertEquals("Error report: " + result.getErrorMessage(), "", result.getErrorMessage()); + + System.out.println("Phase 3.2 - Traversal UNSAT."); + policy = ToscaPolicy.newBuilder().setIdTopologyTemplate(testTemplateId) + .setType(PolicyType.traversal).setSource("host2").setDestination("webserver1").setMiddlebox("fw").build(); + result = client.verifyPolicy(policy); + assertNotNull("There was no response", result); + assertEquals("Unexpected result : " + result.getResult() + " - " + result.getComment(), "UNSAT", result.getResult()); + assertEquals("Error report: " + result.getErrorMessage(), "", result.getErrorMessage()); + + Status resp = client.deleteTopologyTemplate(testTemplateId); + assertEquals("Error while deleting testTemplate", true, resp.getSuccess()); + + System.out.println("Test D completed.\n"); + return; + } + + + @Test + public void test4Deletion() { + System.out.println("\nTest E: Deletion"); + NewTopologyTemplate templ = client.createTopologyTemplate(testTemplate); + + if(templ.getSuccess() != true) { + fail("Unable to create the graph."); + return; + }else { + Status resp = client.deleteTopologyTemplate(templ.getTopologyTemplate().getId()); + assertEquals("Error while deleting testTemplate", true, resp.getSuccess()); + } + + System.out.println("Test E completed.\n"); + return; + } + + + //Generates a correct instance of a TopologyTemplateGrpc to be used in tests + public void generateTestTemplate() { + TopologyTemplateGrpc.Builder templ = TopologyTemplateGrpc.newBuilder(); + List nodes = new ArrayList(); + List relats = new ArrayList(); + + //Definition of nodes + ToscaConfigurationGrpc node0conf = ToscaConfigurationGrpc.newBuilder().setDescription("node0configuration") + .setId("100").setConfiguration("[{\r\n\"webserver1\":\"host2\"\r\n}]").build(); + NodeTemplateGrpc node0 = NodeTemplateGrpc.newBuilder().setConfiguration(node0conf).setId("100") + .setName("fw").setType(Type.firewall).build(); + nodes.add(node0); + + ToscaConfigurationGrpc node1conf = ToscaConfigurationGrpc.newBuilder().setDescription("node1configuration") + .setId("101").setConfiguration("[]").build(); + NodeTemplateGrpc node1 = NodeTemplateGrpc.newBuilder().setConfiguration(node1conf).setId("101") + .setName("webserver1").setType(Type.webserver).build(); + nodes.add(node1); + + ToscaConfigurationGrpc node2conf = ToscaConfigurationGrpc.newBuilder().setDescription("node2configuration") + .setId("102").setConfiguration("[{\r\n\"protocol\":\"HTTP_REQUEST\",\r\n \"url\":\"www.facebook.com\"\r\n }]").build(); + NodeTemplateGrpc node2 = NodeTemplateGrpc.newBuilder().setConfiguration(node2conf).setId("102") + .setName("host2").setType(Type.endhost).build(); + nodes.add(node2); + + ToscaConfigurationGrpc node3conf = ToscaConfigurationGrpc.newBuilder().setDescription("node3configuration") + .setId("103").setConfiguration("[ {\r\n\"protocol\":\"HTTP_REQUEST\",\r\n\"url\":\"www.google.com\",\r\n\"destination\":\"server1\"\r\n}]").build(); + NodeTemplateGrpc node3 = NodeTemplateGrpc.newBuilder().setConfiguration(node3conf).setId("103") + .setName("host1").setType(Type.endhost).build(); + nodes.add(node3); + + ToscaConfigurationGrpc node4conf = ToscaConfigurationGrpc.newBuilder().setDescription("node4configuration") + .setId("104").setConfiguration("[\"host1\",\"host2\"]").build(); + NodeTemplateGrpc node4 = NodeTemplateGrpc.newBuilder().setConfiguration(node4conf).setId("104") + .setName("antispamNode1").setType(Type.antispam).build(); + nodes.add(node4); + + //Building relationships + RelationshipTemplateGrpc rel0 = RelationshipTemplateGrpc.newBuilder().setId("1001") + .setIdSourceNodeTemplate("100").setIdTargetNodeTemplate("101").setName("fwToServ1").build(); + relats.add(rel0); + + RelationshipTemplateGrpc rel1 = RelationshipTemplateGrpc.newBuilder().setId("1002") + .setIdSourceNodeTemplate("101").setIdTargetNodeTemplate("100").setName("serv1ToFw").build(); + relats.add(rel1); + + RelationshipTemplateGrpc rel2 = RelationshipTemplateGrpc.newBuilder().setId("1003") + .setIdSourceNodeTemplate("100").setIdTargetNodeTemplate("103").setName("fwToHost1").build(); + relats.add(rel2); + + RelationshipTemplateGrpc rel3 = RelationshipTemplateGrpc.newBuilder().setId("1004") + .setIdSourceNodeTemplate("100").setIdTargetNodeTemplate("102").setName("fwToHost2").build(); + relats.add(rel3); + + RelationshipTemplateGrpc rel4 = RelationshipTemplateGrpc.newBuilder().setId("1005") + .setIdSourceNodeTemplate("102").setIdTargetNodeTemplate("100").setName("Host2Tofw").build(); + relats.add(rel4); + + RelationshipTemplateGrpc rel5 = RelationshipTemplateGrpc.newBuilder().setId("1006") + .setIdSourceNodeTemplate("103").setIdTargetNodeTemplate("100").setName("Host1Tofw").build(); + relats.add(rel5); + + this.testTemplate = templ.addAllNodeTemplate(nodes).addAllRelationshipTemplate(relats).setId("0").build(); + + TopologyTemplateGrpc.Builder templ2 = TopologyTemplateGrpc.newBuilder(); + List nodes2 = new ArrayList(); + List relats2 = new ArrayList(); + nodes2.add(node0); + nodes2.add(node1); + nodes2.add(node2); + relats2.add(rel0); + relats2.add(rel1); + relats2.add(rel3); + relats2.add(rel4); + + this.simpleTestTemplate = templ2.addAllNodeTemplate(nodes2).addAllRelationshipTemplate(relats2).setId("1").build(); + + } + + /*class NodeTemplateGrpcComparator implements Comparator { + public int compare(NodeTemplateGrpc n0, NodeTemplateGrpc n1) { + return n0.getName().compareTo(n1.getName()); + } + } + + class RelationshipTemplateGrpcComparator implements Comparator { + public int compare(RelationshipTemplateGrpc n0, RelationshipTemplateGrpc n1) { + int source = n0.getIdSourceNodeTemplate().compareTo(n1.getIdSourceNodeTemplate()); + int target = n0.getIdTargetNodeTemplate().compareTo(n1.getIdTargetNodeTemplate()); + return source + target; + } + }*/ + +} diff --git a/verigraph/src/it/polito/verigraph/tosca/converter/grpc/GraphToGrpc.java b/verigraph/src/it/polito/verigraph/tosca/converter/grpc/GraphToGrpc.java new file mode 100644 index 0000000..fb52c7b --- /dev/null +++ b/verigraph/src/it/polito/verigraph/tosca/converter/grpc/GraphToGrpc.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2018 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ +package it.polito.verigraph.tosca.converter.grpc; + +import java.util.Map; + +import it.polito.verigraph.grpc.NodeTemplateGrpc; +import it.polito.verigraph.grpc.RelationshipTemplateGrpc; +import it.polito.verigraph.grpc.TopologyTemplateGrpc; +import it.polito.verigraph.grpc.ToscaConfigurationGrpc; +import it.polito.verigraph.grpc.ToscaTestGrpc; +import it.polito.verigraph.grpc.ToscaVerificationGrpc; +import it.polito.verigraph.model.Configuration; +import it.polito.verigraph.model.Graph; +import it.polito.verigraph.model.Neighbour; +import it.polito.verigraph.model.Node; +import it.polito.verigraph.model.Test; +import it.polito.verigraph.model.Verification; + +public class GraphToGrpc { + + /** Mapping method --> from model Graph to grpc TopologyTemplate */ + public static TopologyTemplateGrpc obtainTopologyTemplate(Graph graph) { + TopologyTemplateGrpc.Builder topol = TopologyTemplateGrpc.newBuilder(); + topol.setId(String.valueOf(graph.getId())); + + //NodeTemplate + for(Node node : graph.getNodes().values()) { + NodeTemplateGrpc nt = obtainNodeTemplate(node); + topol.addNodeTemplate(nt); + //RelationshipTemplate + Map neighMap = node.getNeighbours(); + for (Map.Entry myentry : neighMap.entrySet()) { + Neighbour neigh = myentry.getValue(); + RelationshipTemplateGrpc relat = obtainRelationshipTemplate(neigh, node); + topol.addRelationshipTemplate(relat); + } + } + return topol.build(); + } + + + /** Mapping method --> from model Node to grpc NodeTemplate */ + private static NodeTemplateGrpc obtainNodeTemplate(Node node){ + NodeTemplateGrpc.Builder nodegrpc = NodeTemplateGrpc.newBuilder(); + + nodegrpc.setId(String.valueOf(node.getId())); + nodegrpc.setName(node.getName()); + nodegrpc.setType(NodeTemplateGrpc.Type.valueOf(node.getFunctional_type().toLowerCase())); + + ToscaConfigurationGrpc config = obtainToscaConfiguration(node.getConfiguration()); + nodegrpc.setConfiguration(config); + + return nodegrpc.build(); + } + + + /** Mapping method --> from model Neighbour to grpc RelationshipTemplate */ + private static RelationshipTemplateGrpc obtainRelationshipTemplate(Neighbour neigh, Node sourceNode) { + RelationshipTemplateGrpc.Builder relat = RelationshipTemplateGrpc.newBuilder(); + relat.setId(String.valueOf(sourceNode.getId())); + //Neighbour does not have a neighbourID! RelationshipTemplate does, so it is set to sourceNodeID + relat.setIdSourceNodeTemplate(String.valueOf(sourceNode.getId())); + relat.setIdTargetNodeTemplate(String.valueOf(neigh.getId())); + relat.setName(sourceNode.getName()+"to"+neigh.getName()); + return relat.build(); + } + + + /** Mapping method --> from model Configuration to grpc ToscaConfigurationGrpc */ + private static ToscaConfigurationGrpc obtainToscaConfiguration(Configuration conf) { + return ToscaConfigurationGrpc.newBuilder() + .setId(conf.getId()) + .setDescription(conf.getDescription()) + .setConfiguration(conf.getConfiguration().toString()) + .build(); + } + + /** Mapping method --> from model Verification to grpc ToscaVerificationGrpc */ + public static ToscaVerificationGrpc obtainToscaVerification(Verification verify){ + ToscaVerificationGrpc.Builder ver = ToscaVerificationGrpc.newBuilder(); + ver.setComment(verify.getComment()); + ver.setResult(verify.getResult()); + for(Test test:verify.getTests()){ + ToscaTestGrpc.Builder tst = ToscaTestGrpc.newBuilder().setResult(test.getResult()); + for(Node node:test.getPath()){ + NodeTemplateGrpc nodetempl = obtainNodeTemplate(node); + tst.addNodeTemplate(nodetempl); + } + ver.addTest(tst); + } + return ver.build(); + } + +} diff --git a/verigraph/src/it/polito/verigraph/tosca/converter/grpc/GrpcToGraph.java b/verigraph/src/it/polito/verigraph/tosca/converter/grpc/GrpcToGraph.java new file mode 100644 index 0000000..76906b3 --- /dev/null +++ b/verigraph/src/it/polito/verigraph/tosca/converter/grpc/GrpcToGraph.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2018 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ +package it.polito.verigraph.tosca.converter.grpc; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import it.polito.verigraph.exception.BadRequestException; +import it.polito.verigraph.grpc.NodeTemplateGrpc; +import it.polito.verigraph.grpc.RelationshipTemplateGrpc; +import it.polito.verigraph.grpc.TopologyTemplateGrpc; +import it.polito.verigraph.grpc.ToscaConfigurationGrpc; +import it.polito.verigraph.model.Configuration; +import it.polito.verigraph.model.Graph; +import it.polito.verigraph.model.Neighbour; +import it.polito.verigraph.model.Node; + +public class GrpcToGraph { + + /** Mapping method --> from grpc TopologyTemplateGrpc to model Graph */ + public static Graph deriveGraph(TopologyTemplateGrpc request) throws BadRequestException, JsonProcessingException, IOException { + Graph graph = new Graph(); + Map nodes = new HashMap<>(); + + try { + //Create a list of Node without Neighbour + for(NodeTemplateGrpc nodetempl : request.getNodeTemplateList()){ + Node node = deriveNode(nodetempl); + //It necessary to check uniqueness here otherwise a .put with the same key will overwrite the old node + if(nodes.containsKey(node.getId())) + throw new BadRequestException("The NodeTemplate ID must be unique."); + else + nodes.put(node.getId(), node); + } + + //Add Neighbour to the Node of the list + List relatList = request.getRelationshipTemplateList(); + nodes = deriveNeighboursNode(nodes, relatList); + + //Add Node and ID to the graph + graph.setNodes(nodes); + try { + graph.setId(Long.valueOf(request.getId())); + } catch(NumberFormatException ex) { + throw new BadRequestException("If you want to store your TopologyTemplate on this server," + + "the TopologyTemplate ID must be a number."); + } + + return graph; + + } catch (NullPointerException e) { + throw new BadRequestException("The TopologyTemplate received has invalid fields."); + } + + } + + + /** Mapping method --> from grpc NodeTemplate to model Node (with no Neighbour) */ + private static Node deriveNode(NodeTemplateGrpc nodegrpc) throws BadRequestException, JsonProcessingException, IOException { + Node node = new Node(); + try { + try { + node.setId(Long.valueOf(nodegrpc.getId())); + } catch(NumberFormatException ex) { + throw new BadRequestException("The NodeTemplate ID must be a number."); + } + + node.setName(nodegrpc.getName()); + Configuration conf = deriveConfiguration(nodegrpc.getConfiguration()); + node.setConfiguration(conf); + node.setFunctional_type(nodegrpc.getType().toString()); + + } catch(NullPointerException ex) { + throw new BadRequestException("A NodeTemplate has wrong fields representation."); + } + + return node; + } + + + + /** Mapping method --> from a list of model Node to a list of model Node with their Neighbour */ + private static Map deriveNeighboursNode(Map nodes, List relatList) + throws BadRequestException{ + Map updNodes = nodes; //new list to be filled with updated Node (update = Node + its Neighbour) + for(RelationshipTemplateGrpc relat : relatList) { + try { + //Retrieve the target Node name and generate a new Neighbour + String neighName = updNodes.get(Long.valueOf(relat.getIdTargetNodeTemplate())).getName(); + Neighbour neigh = new Neighbour(); + neigh.setName(neighName); + neigh.setId(Long.valueOf(relat.getId())); + + //Retrieve the Neighbour map of the source Node and add the Neighbour + Node source = updNodes.get(Long.valueOf(relat.getIdSourceNodeTemplate())); + Map sourceNodeNeighMap = source.getNeighbours(); + if(sourceNodeNeighMap.containsKey(neigh.getId())) + throw new BadRequestException("The RelationshipTemplate ID must be unique."); + else + sourceNodeNeighMap.put(neigh.getId(), neigh); + source.setNeighbours(sourceNodeNeighMap); + + //Update the Node list + updNodes.put(Long.valueOf(relat.getIdSourceNodeTemplate()), source); + } catch(NullPointerException | NumberFormatException ex) { + throw new BadRequestException("A RelationshipTemplate has wrong fields representation."); + } + } + return updNodes; + } + + /** Mapping method --> from ToscaConfiguration to model Configuration */ + private static Configuration deriveConfiguration(ToscaConfigurationGrpc request) + throws BadRequestException, JsonProcessingException, IOException { + Configuration conf = new Configuration(); + ObjectMapper mapper = new ObjectMapper(); + JsonNode rootNode = null; + + try { + conf.setId(request.getId()); + } catch (NullPointerException e) {} + + try { + conf.setDescription(request.getDescription()); + } catch (NullPointerException e) {} + + try { + if ("".equals(request.getConfiguration())) + rootNode=mapper.readTree("[]"); + else + rootNode = mapper.readTree(request.getConfiguration()); + } catch (NullPointerException e) { + rootNode=mapper.readTree("[]"); + } + conf.setConfiguration(rootNode); + return conf; + } +} diff --git a/verigraph/src/main/proto/verigraph.proto b/verigraph/src/main/proto/verigraph.proto index feb8ff0..e8e9ccc 100644 --- a/verigraph/src/main/proto/verigraph.proto +++ b/verigraph/src/main/proto/verigraph.proto @@ -1,3 +1,13 @@ +/* This Protocol Buffer has been updated for supporting TOSCA-based objects. + * The only granularity for executing CRUD operations is at the TopologyTemplate level. + * + * The names of the objects are assigned according to the TOSCA standard, and can be + * mapped as follows in the Verigraph domain: + * TopologyTemplate -> Graph + * NodeTemplate -> Node + * RelationshipTemplate -> Neighbour (partial) +*/ + syntax = "proto3"; package verigraph; @@ -6,7 +16,7 @@ option java_multiple_files = true; option java_package = "it.polito.verigraph.grpc"; option java_outer_classname = "VerigraphProto"; -// The service definition. +/** gRPC */ service Verigraph { // Obtains a list of graphs rpc GetGraphs (GetRequest) returns (stream GraphGrpc) {} @@ -45,8 +55,24 @@ service Verigraph { rpc DeleteNeighbour (RequestID) returns (Status) {} // Updates a neighbour rpc UpdateNeighbour (NeighbourGrpc) returns (NewNeighbour) {} + + /** TOSCA gRPC */ + // Obtain a list of topology templates + rpc GetTopologyTemplates (GetRequest) returns (stream TopologyTemplateGrpc) {} + // Obtain a topology template + rpc GetTopologyTemplate (ToscaRequestID) returns (TopologyTemplateGrpc) {} + // Create a TopologyTemplate + rpc CreateTopologyTemplate (TopologyTemplateGrpc) returns (NewTopologyTemplate) {} + // Delete a TopologyTemplate + rpc DeleteTopologyTemplate (ToscaRequestID) returns (Status) {} + // Update a TopologyTemplate + rpc UpdateTopologyTemplate (TopologyTemplateGrpc) returns (NewTopologyTemplate) {} + // Verify a ToscaPolicy + rpc VerifyToscaPolicy (ToscaPolicy) returns (ToscaVerificationGrpc) {} } + +/** Messages */ message GetRequest { } @@ -151,4 +177,87 @@ string errorMessage = 5; message Status{ bool success = 1; string errorMessage = 2; -} \ No newline at end of file +} + +/** TOSCA Messages */ +message ToscaRequestID { + string idTopologyTemplate = 1; +} + +message TopologyTemplateGrpc{ + string id = 1; + string name = 2; + repeated NodeTemplateGrpc nodeTemplate = 3; + repeated RelationshipTemplateGrpc relationshipTemplate = 4; + string errorMessage = 5; +} + +message NewTopologyTemplate{ + bool success = 1; + TopologyTemplateGrpc topologyTemplate = 2; + string errorMessage = 3; +} + +message NodeTemplateGrpc{ + string id = 1; + string name = 2; + enum Type { + antispam = 0; + cache = 1; + dpi = 2; + endhost = 3; + endpoint = 4; + fieldmodifier = 5; + firewall = 6; + mailclient = 7; + mailserver = 8; + nat = 9; + vpnaccess = 10; + vpnexit = 11; + webclient = 12; + webserver = 13; + } + Type type = 3; + ToscaConfigurationGrpc configuration = 4; + string errorMessage = 5; +} + +message RelationshipTemplateGrpc{ + string idSourceNodeTemplate = 1; + string idTargetNodeTemplate = 2; + string id = 3; + string name = 4; + string errorMessage = 5; +} + +message ToscaPolicy{ + string idTopologyTemplate = 1; + string source = 2; + string destination = 3; + enum PolicyType { + reachability = 0; + isolation = 1; + traversal = 2; + } + PolicyType type = 4; + string middlebox = 5; +} + +message ToscaConfigurationGrpc{ + string id = 1; + string description = 2; + string configuration = 3; +} + +message ToscaTestGrpc { + repeated NodeTemplateGrpc nodeTemplate = 1; + string result = 2; +} + +message ToscaVerificationGrpc{ + bool successOfOperation = 1; + string result = 2; + string comment = 3; + repeated ToscaTestGrpc test = 4; + string errorMessage = 5; +} -- 2.16.6