GNPy integration
Installs the path with the best OSRn value according to gnpy, while also setting the power at each stage.

usage:
odtn-connect-gnpy-command http 127.0.0.1 8080 test test
odtn-gnpy-connection-command -b netconf:127.0.0.1:1101/1 netconf:127.0.0.1:1102/1

Change-Id: I82b6d57d07ab5a3873ce37eb44639ebaa85c733f
diff --git a/apps/odtn/service/BUILD b/apps/odtn/service/BUILD
index abf6ee6..5226c66 100644
--- a/apps/odtn/service/BUILD
+++ b/apps/odtn/service/BUILD
@@ -5,6 +5,17 @@
     "//apps/yang:onos-apps-yang",
     "//apps/optical-model:onos-apps-optical-model",
     "//protocols/netconf/api:onos-protocols-netconf-api",
+    "@jersey_client//jar",
+    "@jersey_server//jar",
+    "@jersey_common//jar",
+    "@jersey_security//jar",
+    "@httpclient_osgi//jar",
+    "@httpcore_osgi//jar",
+    "@javax_ws_rs_api//jar",
+    "@hk2_api//jar",
+    "@aopalliance_repackaged//jar",
+    "@javax_inject//jar",
+    "//utils/rest:onlab-rest",
 ]
 
 osgi_jar_with_tests(
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/GnpyService.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/GnpyService.java
new file mode 100644
index 0000000..4016b3e
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/GnpyService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.odtn;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.IntentId;
+
+/**
+ * Interface for Integration with GNPy optical tool.
+ */
+public interface GnpyService {
+
+
+    /**
+     * Connectes to an instance of GNPY optical planning and simulation tool.
+     *
+     * @param protocol the protocol, eg HTTP or HTTPS
+     * @param ip       the ip of the service
+     * @param port     the port fo the service
+     * @param username the username of the service
+     * @param password the password.
+     * @return true if connection was successful
+     */
+    boolean connectGnpy(String protocol, String ip, String port, String username, String password);
+
+    /**
+     * Disconnects for the connected GNPy instance.
+     *
+     * @return true if successful
+     */
+    boolean disconnectGnpy();
+
+    /**
+     * Checks connectivity an instance of GNPY optical planning and simulation tool.
+     *
+     * @return true if connection was successful
+     */
+    boolean isConnected();
+
+    /**
+     * Obtains the best connectivity from A to B according to GSNR from GNPY, possibly bidirectional.
+     *
+     * @param ingress       the ingress connect point
+     * @param egress        the egress connect point
+     * @param bidirectional true if bidirectional connectivity is required.
+     * @return the Id of the intent that was installed as a result of the computation.
+     */
+    Pair<IntentId, Double> obtainConnectivity(ConnectPoint ingress, ConnectPoint egress, boolean bidirectional);
+
+
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/cli/impl/ConnectGnpyCommand.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/cli/impl/ConnectGnpyCommand.java
new file mode 100644
index 0000000..a397b9f
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/cli/impl/ConnectGnpyCommand.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.odtn.cli.impl;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.odtn.GnpyService;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Beta
+@Service
+@Command(scope = "onos", name = "odtn-connect-gnpy-command",
+        description = "show tapi context command")
+public class ConnectGnpyCommand extends AbstractShellCommand {
+
+    private static final Logger log = getLogger(ConnectGnpyCommand.class);
+
+    @Argument(index = 0, name = "protocol",
+            description = "protocol for requests, e.g. HTTP or HTTPS",
+            required = true, multiValued = false)
+    String protocol = "";
+
+    @Argument(index = 1, name = "ip",
+            description = "Ip of GNPy instance",
+            required = true, multiValued = false)
+    String ip = "";
+
+    @Argument(index = 2, name = "port",
+            description = "Protocol of GNPy instance",
+            required = true, multiValued = false)
+    String port = "";
+
+    @Argument(index = 3, name = "username",
+            description = "Username of GNPy instance",
+            required = true, multiValued = false)
+    String username = "";
+
+    @Argument(index = 4, name = "password",
+            description = "Password of GNPy instance",
+            required = true, multiValued = false)
+    String password = "";
+
+    @Option(name = "-d", aliases = "--disconnect",
+            description = "If this argument is passed the connection with gNPY is removed.",
+            required = false, multiValued = false)
+    private boolean disconnect = false;
+
+    @Override
+    protected void doExecute() {
+        GnpyService service = get(GnpyService.class);
+        String msg;
+        if (disconnect) {
+            msg = service.disconnectGnpy() ? "gnpy disconnect" : "error in disconnecting gnpy";
+        } else {
+            Preconditions.checkNotNull(ip, "Ip must be specified");
+            Preconditions.checkNotNull(password, "password must be specified");
+            msg = service.connectGnpy(protocol, ip, port, username, password) ? "gnpy connected" :
+                    "error in connecting gnpy, please check logs";
+        }
+        print(msg);
+    }
+
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/cli/impl/GnpyConnectionRequestCommand.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/cli/impl/GnpyConnectionRequestCommand.java
new file mode 100644
index 0000000..c434fdb
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/cli/impl/GnpyConnectionRequestCommand.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.odtn.cli.impl;
+
+import com.google.common.annotations.Beta;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.ConnectPointCompleter;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.odtn.GnpyService;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Beta
+@Service
+@Command(scope = "onos", name = "odtn-gnpy-connection-command",
+        description = "show tapi context command")
+public class GnpyConnectionRequestCommand extends AbstractShellCommand {
+
+    private static final Logger log = getLogger(GnpyConnectionRequestCommand.class);
+
+    @Argument(index = 0, name = "ingress",
+            description = "Ingress Device/Port Description",
+            required = true, multiValued = false)
+    @Completion(ConnectPointCompleter.class)
+    String ingressString = "";
+
+    @Argument(index = 1, name = "egress",
+            description = "Egress Device/Port Description",
+            required = true, multiValued = false)
+    @Completion(ConnectPointCompleter.class)
+    String egressString = "";
+
+    @Option(name = "-b", aliases = "--bidirectional",
+            description = "If this argument is passed the optical link created will be bidirectional, " +
+                    "else the link will be unidirectional.",
+            required = false, multiValued = false)
+    private boolean bidirectional = false;
+
+    @Override
+    protected void doExecute() {
+        GnpyService service = get(GnpyService.class);
+
+        if (!service.isConnected()) {
+            print("gNPY is not connected, please issue `odtn-connect-gnpy-command` first");
+            return;
+        }
+
+        ConnectPoint ingress = createConnectPoint(ingressString);
+        ConnectPoint egress = createConnectPoint(egressString);
+
+        Pair<IntentId, Double> intentIdAndOsnr =
+                service.obtainConnectivity(ingress, egress, bidirectional);
+
+        if (intentIdAndOsnr != null) {
+            print("Optical Connectivity from %s to %s submitted through GNPy. \n", ingress, egress);
+            print("Expected GSNR %.2f dB", intentIdAndOsnr.getRight().doubleValue());
+            print("Intent: %s", intentIdAndOsnr.getLeft());
+        } else {
+            print("Error in submitting Optical Connectivity submitted through GNPy, please see logs");
+        }
+    }
+
+    private ConnectPoint createConnectPoint(String devicePortString) {
+        String[] splitted = devicePortString.split("/");
+
+        checkArgument(splitted.length == 2,
+                      "Connect point must be in \"deviceUri/portNumber\" format");
+
+        return ConnectPoint.deviceConnectPoint(devicePortString);
+    }
+
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/GnpyManager.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/GnpyManager.java
new file mode 100644
index 0000000..a474dfa
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/GnpyManager.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.odtn.impl;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.google.common.annotations.Beta;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onlab.graph.ScalarWeight;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Direction;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Link;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.Path;
+import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.odtn.GnpyService;
+import org.onosproject.store.service.AtomicCounter;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.MediaType;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import static java.lang.Math.log10;
+import static org.onosproject.net.ChannelSpacing.CHL_50GHZ;
+import static org.onosproject.net.ChannelSpacing.CHL_6P25GHZ;
+import static org.onosproject.net.optical.util.OpticalIntentUtility.createOpticalIntent;
+
+/**
+ * Implementation of GnpyService.
+ */
+@Beta
+@Component(immediate = true, service = GnpyService.class)
+public class GnpyManager implements GnpyService {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected IntentService intentService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected LinkService linkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected StorageService storageService;
+
+    HttpUtil gnpyHttpUtil;
+
+    private static final String APP_ID = "org.onosproject.odtn-service";
+
+    private static final ProviderId PROVIDER_ID = new ProviderId("odtn", "gnpy");
+
+    private ApplicationId appId;
+
+    private AtomicCounter counter;
+
+    private Map<IntentId, GnpyPowerInfo> intentsPowerMap = new HashMap<>();
+
+    @Activate
+    protected void activate() {
+        log.info("Started");
+        appId = coreService.getAppId(APP_ID);
+        counter = storageService.getAtomicCounter("GNPy-connection-counter");
+    }
+
+
+    @Override
+    public boolean connectGnpy(String protocol, String ip, String port, String username, String password) {
+        gnpyHttpUtil = new HttpUtil(protocol, ip, port);
+        gnpyHttpUtil.connect(username, password);
+        return gnpyHttpUtil.get("/gnpy-experimental", MediaType.APPLICATION_JSON_TYPE) != null;
+    }
+
+    @Override
+    public boolean disconnectGnpy() {
+        gnpyHttpUtil.disconnect();
+        gnpyHttpUtil = null;
+        return true;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return gnpyHttpUtil != null;
+    }
+
+    @Override
+    public Pair<IntentId, Double> obtainConnectivity(ConnectPoint ingress, ConnectPoint egress, boolean bidirectional) {
+        ByteArrayOutputStream connectivityRequest = createGnpyRequest(ingress, egress, bidirectional);
+        String response = gnpyHttpUtil.post(null, "/gnpy-experimental",
+                                            new ByteArrayInputStream(connectivityRequest.toByteArray()),
+                                            MediaType.APPLICATION_JSON_TYPE, String.class);
+        ObjectMapper om = new ObjectMapper();
+        final ObjectReader reader = om.reader();
+        JsonNode jsonNode;
+        try {
+            jsonNode = reader.readTree(response);
+            if (jsonNode == null) {
+                log.error("JsonNode is null for response {}", response);
+                return null;
+            }
+            log.info("Response {}", response);
+            String bestPath;
+            try {
+                bestPath = getBestOsnrPathKey(jsonNode);
+            } catch (IllegalStateException e) {
+                log.error("Exception while contacting GNPy", e);
+                return null;
+            }
+            OchSignal ochSignal = createOchSignal(jsonNode);
+            Map<DeviceId, Double> deviceAtoBPowerMap = new HashMap<>();
+            Map<DeviceId, Double> deviceBtoAPowerMap = new HashMap<>();
+            //TODO this list is currently only populated in the forward direction
+            List<DeviceId> deviceIds = getDeviceAndPopulatePowerMap(jsonNode, deviceAtoBPowerMap,
+                                                                    deviceBtoAPowerMap, bestPath);
+            Path suggestedPath = createSuggestedPath(deviceIds);
+            log.info("Suggested path {}", suggestedPath);
+
+            Intent intent = createOpticalIntent(ingress, egress, deviceService,
+                                                null, appId, bidirectional, ochSignal, suggestedPath);
+
+            intentsPowerMap.put(intent.id(), new GnpyPowerInfo(deviceAtoBPowerMap, deviceBtoAPowerMap,
+                                                               getLaunchPower(jsonNode), suggestedPath.links(),
+                                                               ingress, egress, ochSignal));
+            intentService.submit(intent);
+            return Pair.of(intent.id(), getOsnr(jsonNode, bestPath));
+        } catch (IOException e) {
+            log.error("Exception while reading response {}", response, e);
+            return null;
+        }
+    }
+
+    private String getBestOsnrPathKey(JsonNode connectivityReply) throws IllegalStateException {
+        Double bestOsnr = -100.0;
+        String bestPathId = "";
+        if (connectivityReply.get("result").asText().contains("Service error")) {
+            throw new IllegalStateException(connectivityReply.get("result").asText());
+        }
+        Iterator<JsonNode> paths = connectivityReply.get("result").get("response")
+                .elements();
+        while (paths.hasNext()) {
+            JsonNode path = paths.next();
+            String respId = path.get("response-id").asText();
+            double osnr = getOsnr(connectivityReply, respId);
+            if (osnr > bestOsnr) {
+                bestOsnr = osnr;
+                bestPathId = respId;
+            }
+        }
+        return bestPathId;
+    }
+
+    protected Path createSuggestedPath(List<DeviceId> deviceIds) {
+        List<Link> listLinks = new ArrayList<>();
+        for (int i = 0; i < deviceIds.size() - 1; i++) {
+            Set<Link> links = linkService.getDeviceLinks(deviceIds.get(i));
+
+            for (Link link : links) {
+                if (link.dst().deviceId().equals(deviceIds.get(i + 1))) {
+                    listLinks.add(link);
+                }
+            }
+        }
+        return new DefaultPath(PROVIDER_ID, listLinks, new ScalarWeight(1));
+
+    }
+
+    protected List<DeviceId> getDeviceAndPopulatePowerMap(JsonNode connectivityReply,
+                                                          Map<DeviceId, Double> deviceAtoBPowerMap,
+                                                          Map<DeviceId, Double> deviceBtoAPowerMap,
+                                                          String name) {
+        List<DeviceId> deviceIds = new ArrayList<>();
+        if (connectivityReply.has("result")
+                && connectivityReply.get("result").has("response")) {
+            JsonNode response = connectivityReply.get("result").get("response");
+            //getting the a-b path.
+            Iterator<JsonNode> paths = connectivityReply.get("result").get("response")
+                    .elements();
+            while (paths.hasNext()) {
+                JsonNode path = paths.next();
+                if (path.get("response-id").asText().equals(name)) {
+                    Iterator<JsonNode> elements = path.get("path-properties")
+                            .get("reversed-path-route-objects").elements();
+                    Iterable<JsonNode> iterable = () -> elements;
+                    List<JsonNode> elementsList = StreamSupport
+                            .stream(iterable.spliterator(), false)
+                            .collect(Collectors.toList());
+                    Iterator<JsonNode> reversePathRoute = path.get("path-properties")
+                            .get("reversed-path-route-objects").elements();
+                    Iterable<JsonNode> reversedIterable = () -> reversePathRoute;
+                    List<JsonNode> reversedElementsList = StreamSupport
+                            .stream(reversedIterable.spliterator(), false)
+                            .collect(Collectors.toList());
+                    for (int i = 0; i < elementsList.size() - 1; i++) {
+                        if (elementsList.get(i).get("path-route-object").has("num-unnum-hop")) {
+                            String elementId = elementsList.get(i).get("path-route-object")
+                                    .get("num-unnum-hop").get("node-id")
+                                    .asText();
+                            //TODO this is a workaround until we understand better the
+                            // topology mapping between ONOS and GNPy
+                            if (elementId.startsWith("netconf:")) {
+                                double power = -99;
+                                if (!elementsList.get(i).get("path-route-object")
+                                        .get("num-unnum-hop").get("gnpy-node-type")
+                                        .asText().equals("transceiver")) {
+                                    power = getPerHopPower(elementsList.get(i + 2));
+                                }
+                                deviceAtoBPowerMap.put(DeviceId.deviceId(elementId), power);
+                                for (int j = 0; j < reversedElementsList.size() - 1; j++) {
+                                    if (reversedElementsList.get(j).get("path-route-object").has("num-unnum-hop")) {
+                                        String reversedElementId = reversedElementsList.get(j).get("path-route-object")
+                                                .get("num-unnum-hop").get("node-id")
+                                                .asText();
+                                        double reversePower = -99;
+                                        if (reversedElementId.equals(elementId)) {
+                                            reversePower = getPerHopPower(reversedElementsList.get(j + 2));
+                                            deviceBtoAPowerMap.put(DeviceId.deviceId(elementId), reversePower);
+                                        }
+                                    }
+                                }
+                                deviceIds.add(DeviceId.deviceId(elementId));
+                            }
+                        }
+                    }
+                    break;
+                }
+            }
+        } else {
+            log.warn("Can't retrieve devices {}", connectivityReply);
+        }
+        return deviceIds;
+    }
+
+    protected OchSignal createOchSignal(JsonNode connectivityReply) throws IllegalArgumentException {
+        if (connectivityReply.has("result")
+                && connectivityReply.get("result").has("response")) {
+            Iterator<JsonNode> elements = connectivityReply.get("result").get("response").elements()
+                    .next().get("path-properties").get("path-route-objects").elements();
+            Iterable<JsonNode> iterable = () -> elements;
+            List<JsonNode> elementsList = StreamSupport
+                    .stream(iterable.spliterator(), false)
+                    .collect(Collectors.toList());
+            int n = 0;
+            int m = 0;
+            for (JsonNode node : elementsList) {
+                if (node.get("path-route-object").has("label-hop")) {
+                    n = node.get("path-route-object").get("label-hop").get("N").asInt();
+                    m = node.get("path-route-object").get("label-hop").get("M").asInt();
+                    break;
+                }
+            }
+            int offset = 193100;
+
+            double centralFreq = offset + (n * CHL_6P25GHZ.frequency().asGHz());
+            try {
+                int multiplier = getMultplier(centralFreq, GridType.DWDM, CHL_50GHZ);
+                return new OchSignal(GridType.DWDM, CHL_50GHZ, multiplier, 4);
+            } catch (RuntimeException e) {
+                /* catching RuntimeException as both NullPointerException (thrown by
+                 * checkNotNull) and IllegalArgumentException (thrown by checkArgument)
+                 * are subclasses of RuntimeException.
+                 */
+                throw new IllegalArgumentException(e);
+            }
+        }
+        return null;
+    }
+
+    protected double getLaunchPower(JsonNode connectivityReply) {
+        double power = -99;
+        if (connectivityReply.has("result")
+                && connectivityReply.get("result").has("response")) {
+            Iterator<JsonNode> elements = connectivityReply.get("result").get("response")
+                    .elements().next().get("path-properties").get("path-metric").elements();
+            Iterable<JsonNode> iterable = () -> elements;
+            List<JsonNode> elementsList = StreamSupport
+                    .stream(iterable.spliterator(), false)
+                    .collect(Collectors.toList());
+            for (JsonNode node : elementsList) {
+                if (node.has("metric-type") &&
+                        node.get("metric-type").asText().equals("reference_power")) {
+                    power = node.get("accumulative-value").asDouble();
+                    break;
+                }
+            }
+        }
+        return 10 * log10(power * 1000);
+    }
+
+    protected double getPerHopPower(JsonNode pathRouteObj) {
+        double power = -99;
+        if (pathRouteObj.get("path-route-object").has("target-channel-power")) {
+            power = pathRouteObj.get("path-route-object")
+                    .get("target-channel-power").get("value")
+                    .asDouble();
+        }
+        return power;
+    }
+
+    protected double getOsnr(JsonNode connectivityReply, String name) {
+        double osnr = -1;
+        if (connectivityReply.has("result")
+                && connectivityReply.get("result").has("response")) {
+            Iterator<JsonNode> paths = connectivityReply.get("result").get("response")
+                    .elements();
+            while (paths.hasNext()) {
+                JsonNode path = paths.next();
+                if (path.get("response-id").asText().equals(name)) {
+                    Iterator<JsonNode> elements = path.get("path-properties").get("path-metric").elements();
+                    Iterable<JsonNode> iterable = () -> elements;
+                    List<JsonNode> elementsList = StreamSupport
+                            .stream(iterable.spliterator(), false)
+                            .collect(Collectors.toList());
+                    for (JsonNode node : elementsList) {
+                        if (node.has("metric-type") &&
+                                node.get("metric-type").asText().equals("OSNR-0.1nm")) {
+                            osnr = node.get("accumulative-value").asDouble();
+                            break;
+                        }
+                    }
+                    if (osnr != -1) {
+                        break;
+                    }
+                }
+            }
+        }
+        return osnr;
+    }
+
+    private int getMultplier(double wavelength, GridType gridType, ChannelSpacing channelSpacing) {
+        long baseFreq;
+        switch (gridType) {
+            case DWDM:
+                baseFreq = 193100;
+                break;
+            case CWDM:
+            case FLEX:
+            case UNKNOWN:
+            default:
+                baseFreq = 0L;
+                break;
+        }
+        return (int) ((wavelength - baseFreq) / (channelSpacing.frequency().asGHz()));
+    }
+
+    protected ByteArrayOutputStream createGnpyRequest(ConnectPoint ingress,
+                                                      ConnectPoint egress, boolean bidirectional) {
+        /*
+        {
+  "path-request": [
+    {
+      "request-id": "first",
+      "source": "trx-Amsterdam",
+      "destination": "trx-Bremen",
+      "src-tp-id": "trx-Amsterdam",
+      "dst-tp-id": "trx-Bremen",
+      "bidirectional": false,
+      "path-constraints": {
+        "te-bandwidth": {
+          "technology": "flexi-grid",
+          "trx_type": "Voyager",
+          "trx_mode": null,
+          "effective-freq-slot": [
+            {
+              "N": "null",
+              "M": "null"
+            }
+          ],
+          "spacing": 50000000000.0,
+          "max-nb-of-channel": null,
+          "output-power": null,
+          "path_bandwidth": 100000000000.0
+        }
+      }
+    }
+    ]
+    }
+         */
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        try {
+            JsonGenerator generator = getJsonGenerator(stream);
+            generator.writeStartObject();
+            generator.writeArrayFieldStart("path-request");
+            generator.writeStartObject();
+            generator.writeStringField("request-id", "onos-" + counter.getAndIncrement());
+            generator.writeStringField("source", ingress.deviceId().toString());
+            generator.writeStringField("destination", egress.deviceId().toString());
+            generator.writeStringField("src-tp-id", ingress.deviceId().toString());
+            generator.writeStringField("dst-tp-id", egress.deviceId().toString());
+            generator.writeBooleanField("bidirectional", bidirectional);
+            generator.writeObjectFieldStart("path-constraints");
+            generator.writeObjectFieldStart("te-bandwidth");
+            generator.writeStringField("technology", "flexi-grid");
+            generator.writeStringField("trx_type", "Cassini"); //TODO make variable
+            generator.writeNullField("trx_mode");
+            generator.writeArrayFieldStart("effective-freq-slot");
+            generator.writeStartObject();
+            generator.writeStringField("N", "null");
+            generator.writeStringField("M", "null");
+            generator.writeEndObject();
+            generator.writeEndArray();
+            generator.writeNumberField("spacing", 50000000000.0);
+            generator.writeNullField("max-nb-of-channel");
+            generator.writeNullField("output-power");
+            generator.writeNumberField("path_bandwidth", 100000000000.0);
+            generator.writeEndObject();
+            generator.writeEndObject();
+            generator.writeEndObject();
+            generator.writeEndArray();
+            generator.writeEndObject();
+            generator.close();
+            return stream;
+        } catch (IOException e) {
+            log.error("Cant' create json", e);
+        }
+        return stream;
+
+    }
+
+    private JsonGenerator getJsonGenerator(ByteArrayOutputStream stream) throws IOException {
+        JsonFactory factory = new JsonFactory();
+        return factory.createGenerator(stream, JsonEncoding.UTF8);
+    }
+
+    /**
+     * Internal listener for tracking the intent deletion events.
+     */
+    private class InternalIntentListener implements IntentListener {
+
+        @Override
+        public boolean isRelevant(IntentEvent event) {
+            return intentsPowerMap.keySet().contains(event.subject().id());
+        }
+
+        @Override
+        public void event(IntentEvent event) {
+            setPathPower(event.subject());
+
+        }
+    }
+
+    private void setPathPower(Intent intent) {
+        GnpyPowerInfo powerInfo = intentsPowerMap.get(intent.id());
+        for (Link link : powerInfo.path()) {
+            Device ingressDev = deviceService.getDevice(link.src().deviceId());
+            if (ingressDev.is(PowerConfig.class)) {
+                if (powerInfo.deviceAtoBPowerMap().get(link.src().deviceId()) != -99) {
+                    log.info("Configuring power {} for {}",
+                             powerInfo.deviceAtoBPowerMap().get(link.src().deviceId()),
+                             link.src().deviceId());
+                    ingressDev.as(PowerConfig.class)
+                            .setTargetPower(link.src().port(), powerInfo.ochSignal(),
+                                            powerInfo.deviceAtoBPowerMap()
+                                                    .get(link.src().deviceId()));
+                } else {
+                    log.warn("Can't determine power for {}", link.src().deviceId());
+                }
+            }
+            Device egressDev = deviceService.getDevice(link.dst().deviceId());
+            if (egressDev.is(PowerConfig.class)) {
+                if (powerInfo.deviceBtoAPowerMap().get(link.dst().deviceId()) != -99) {
+                    log.info("Configuring power {} for {}",
+                             powerInfo.deviceBtoAPowerMap().get(link.dst().deviceId()),
+                             link.dst().deviceId());
+                    egressDev.as(PowerConfig.class)
+                            .setTargetPower(link.dst().port(), powerInfo.ochSignal(),
+                                            powerInfo.deviceBtoAPowerMap()
+                                                    .get(link.dst().deviceId()));
+                } else {
+                    log.warn("Can't determine power for {}", link.dst().deviceId());
+                }
+            }
+        }
+        Device ingressDevice = deviceService.getDevice(powerInfo.ingress().deviceId());
+        if (ingressDevice.is(PowerConfig.class)) {
+            if (powerInfo.launchPower() != -99) {
+                log.info("Configuring ingress with power {} for {}",
+                         powerInfo.launchPower(), ingressDevice);
+                ingressDevice.as(PowerConfig.class)
+                        .setTargetPower(powerInfo.ingress().port(), Direction.ALL, powerInfo.launchPower());
+            }
+        }
+        Device egressDevice = deviceService.getDevice(powerInfo.ingress().deviceId());
+        if (egressDevice.is(PowerConfig.class)) {
+            if (powerInfo.launchPower() != -99) {
+                log.info("Configuring egress with power {} for {}",
+                         powerInfo.launchPower(), ingressDevice);
+                ingressDevice.as(PowerConfig.class)
+                        .setTargetPower(powerInfo.ingress().port(), Direction.ALL, powerInfo.launchPower());
+            }
+        }
+    }
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/GnpyPowerInfo.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/GnpyPowerInfo.java
new file mode 100644
index 0000000..2923ec0
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/GnpyPowerInfo.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.odtn.impl;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.OchSignal;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class contains information given by gNPY for a given path.
+ */
+@Beta
+public final class GnpyPowerInfo {
+
+    private Map<DeviceId, Double> deviceAtoBPowerMap;
+    private Map<DeviceId, Double> deviceBtoAPowerMap;
+    private double launchPower;
+    private List<Link> path;
+    private ConnectPoint ingress;
+    private ConnectPoint egress;
+    private OchSignal ochSignal;
+
+    /**
+     * Creates the class with information.
+     *
+     * @param deviceAtoBPowerMap the power in a to b direction
+     * @param deviceBtoAPowerMap the power in b to a direction
+     * @param launchPower        the power at the TXs
+     * @param path               the pat
+     * @param ingress            the ingress device (A)
+     * @param egress             the egress device (B)
+     * @param ochSignal          the signal
+     */
+    public GnpyPowerInfo(Map<DeviceId, Double> deviceAtoBPowerMap,
+                         Map<DeviceId, Double> deviceBtoAPowerMap, double launchPower,
+                         List<Link> path, ConnectPoint ingress, ConnectPoint egress,
+                         OchSignal ochSignal) {
+        this.deviceAtoBPowerMap = deviceAtoBPowerMap;
+        this.deviceBtoAPowerMap = deviceBtoAPowerMap;
+        this.launchPower = launchPower;
+        this.path = path;
+        this.ingress = ingress;
+        this.egress = egress;
+        this.ochSignal = ochSignal;
+    }
+
+    /**
+     * Retrieve the ingress connect point of the path (A).
+     *
+     * @return ingress connect point
+     */
+    public ConnectPoint ingress() {
+        return ingress;
+    }
+
+    /**
+     * Retrieve the egress connect point of the path (B).
+     *
+     * @return egress connect point
+     */
+    public ConnectPoint egress() {
+        return egress;
+    }
+
+    /**
+     * Retrieve the ochSignal.
+     *
+     * @return signal
+     */
+    public OchSignal ochSignal() {
+        return ochSignal;
+    }
+
+    /**
+     * Retrieve the the power in a to b direction.
+     *
+     * @return power map a to b
+     */
+    public Map<DeviceId, Double> deviceAtoBPowerMap() {
+        return deviceAtoBPowerMap;
+    }
+
+    /**
+     * Retrieve the power in b to a direction.
+     *
+     * @return power map b to a
+     */
+    public Map<DeviceId, Double> deviceBtoAPowerMap() {
+        return deviceBtoAPowerMap;
+    }
+
+    /**
+     * Retrieve the launch power at the TX, both A and B.
+     *
+     * @return ingress connect point
+     */
+    public double launchPower() {
+        return launchPower;
+    }
+
+    /**
+     * Retrieve the set of links for the path in the network.
+     *
+     * @return links fo the path
+     */
+    public List<Link> path() {
+        return path;
+    }
+}
diff --git a/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/HttpUtil.java b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/HttpUtil.java
new file mode 100644
index 0000000..1da0e5d
--- /dev/null
+++ b/apps/odtn/service/src/main/java/org/onosproject/odtn/impl/HttpUtil.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.odtn.impl;
+
+import com.google.common.annotations.Beta;
+import org.apache.commons.io.IOUtils;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+/**
+ * The implementation of HttpUtils.
+ */
+@Beta
+public class HttpUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
+    private static final String XML = "xml";
+    private static final String JSON = "json";
+    protected static final String DOUBLESLASH = "//";
+    protected static final String COLON = ":";
+    private static final int STATUS_OK = Response.Status.OK.getStatusCode();
+    private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode();
+    private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
+    private static final String HTTPS = "https";
+
+    private Client client = null;
+    private String protocol = null;
+    private String ip = null;
+    private String port = null;
+
+    public HttpUtil(String protocol, String ip, String port) {
+        //TODO check not null
+        this.protocol = protocol.equals("") ? HTTPS : protocol;
+        this.ip = ip;
+        this.port = port;
+    }
+
+    public void connect(String username, String password) {
+        client = ignoreSslClient();
+        authenticate(client, username, password);
+    }
+
+    public void disconnect() {
+        protocol = "";
+        ip = "";
+        port = "";
+        client = null;
+    }
+
+    public int post(String request, InputStream payload, MediaType mediaType) {
+        Response response = getResponse(request, payload, mediaType);
+        if (response == null) {
+            return Status.NO_CONTENT.getStatusCode();
+        }
+        return response.getStatus();
+    }
+
+    public <T> T post(DeviceId device, String request, InputStream payload, MediaType mediaType,
+                      Class<T> responseClass) {
+        Response response = getResponse(request, payload, mediaType);
+        if (response != null && response.hasEntity()) {
+            // Do not read the entity if the responseClass is of type Response. This would allow the
+            // caller to receive the Response directly and try to read its appropriate entity locally.
+            return responseClass == Response.class ? (T) response : response.readEntity(responseClass);
+        }
+        log.error("Response from device {} for request {} contains no entity", device, request);
+        return null;
+    }
+
+    private Response getResponse(String request, InputStream payload, MediaType mediaType) {
+
+        WebTarget wt = getWebTarget(request);
+
+        Response response = null;
+        if (payload != null) {
+            try {
+                response = wt.request(mediaType)
+                        .post(Entity.entity(IOUtils.toString(payload, StandardCharsets.UTF_8), mediaType));
+            } catch (IOException e) {
+                log.error("Cannot do POST {} request on GNPY because can't read payload", request);
+            }
+        } else {
+            response = wt.request(mediaType).post(Entity.entity(null, mediaType));
+        }
+        return response;
+    }
+
+    public int put(String request, InputStream payload, MediaType mediaType) {
+
+        WebTarget wt = getWebTarget(request);
+
+        Response response = null;
+        if (payload != null) {
+            try {
+                response = wt.request(mediaType).put(Entity.entity(IOUtils.
+                        toString(payload, StandardCharsets.UTF_8), mediaType));
+            } catch (IOException e) {
+                log.error("Cannot do POST {} request on GNPY because can't read payload", request);
+            }
+        } else {
+            response = wt.request(mediaType).put(Entity.entity(null, mediaType));
+        }
+
+        if (response == null) {
+            return Status.NO_CONTENT.getStatusCode();
+        }
+        return response.getStatus();
+    }
+
+    public InputStream get(String request, MediaType mediaType) {
+        WebTarget wt = getWebTarget(request);
+
+        Response s = wt.request(mediaType).get();
+
+        if (checkReply(s)) {
+            return new ByteArrayInputStream(s.readEntity((String.class)).getBytes(StandardCharsets.UTF_8));
+        }
+        return null;
+    }
+
+    public int delete(DeviceId device, String request, InputStream payload, MediaType mediaType) {
+
+        WebTarget wt = getWebTarget(request);
+
+        // FIXME: do we need to delete an entry by enclosing data in DELETE
+        // request?
+        // wouldn't it be nice to use PUT to implement the similar concept?
+        Response response = null;
+        try {
+            response = wt.request(mediaType).delete();
+        } catch (ProcessingException procEx) {
+            log.error("Cannot issue DELETE {} request on device {}", request, device);
+            return Status.SERVICE_UNAVAILABLE.getStatusCode();
+        }
+
+        return response.getStatus();
+    }
+
+    private void authenticate(Client client, String username, String password) {
+        client.register(HttpAuthenticationFeature.basic(username, password));
+
+    }
+
+    protected WebTarget getWebTarget(String request) {
+        log.debug("Sending request to URL {} ", getUrlString(request));
+        return client.target(getUrlString(request));
+    }
+
+    protected String getUrlString(String request) {
+        return protocol + COLON + DOUBLESLASH + ip + COLON + port + request;
+    }
+
+    private boolean checkReply(Response response) {
+        if (response != null) {
+            boolean statusCode = checkStatusCode(response.getStatus());
+            if (!statusCode && response.hasEntity()) {
+                log.error("Failed request, HTTP error msg : " + response.readEntity(String.class));
+            }
+            return statusCode;
+        }
+        log.error("Null reply from device");
+        return false;
+    }
+
+    private boolean checkStatusCode(int statusCode) {
+        if (statusCode == STATUS_OK || statusCode == STATUS_CREATED || statusCode == STATUS_ACCEPTED) {
+            return true;
+        } else {
+            log.error("Failed request, HTTP error code : " + statusCode);
+            return false;
+        }
+    }
+
+    private Client ignoreSslClient() {
+        SSLContext sslcontext = null;
+
+        try {
+            sslcontext = SSLContext.getInstance("TLS");
+            sslcontext.init(null, new TrustManager[]{new X509TrustManager() {
+                @Override
+                public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
+                }
+
+                @Override
+                public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
+                }
+
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return new X509Certificate[0];
+                }
+            } }, new java.security.SecureRandom());
+        } catch (NoSuchAlgorithmException | KeyManagementException e) {
+            throw new IllegalStateException(e);
+        }
+
+        return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build();
+    }
+
+}
diff --git a/apps/odtn/service/src/main/resources/gnpy-response.json b/apps/odtn/service/src/main/resources/gnpy-response.json
new file mode 100644
index 0000000..2446e4b
--- /dev/null
+++ b/apps/odtn/service/src/main/resources/gnpy-response.json
@@ -0,0 +1,1010 @@
+{
+  "result": {
+    "response": [
+      {
+        "response-id": "first",
+        "path-properties": {
+          "path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "z-a-path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "path-route-objects": [
+            {
+              "path-route-object": {
+                "index": 0,
+                "num-unnum-hop": {
+                  "node-id": "trx-Amsterdam",
+                  "link-tp-id": "trx-Amsterdam",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 1,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 2,
+                "transponder": {
+                  "transponder-type": "Voyager",
+                  "transponder-mode": "mode 1"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 3,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-AD",
+                  "link-tp-id": "roadm-Amsterdam-AD",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 4,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 5,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 6,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))",
+                  "link-tp-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 7,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 8,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2)",
+                  "link-tp-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 9,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 10,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))-(roadm-Amsterdam-L2)",
+                  "link-tp-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))-(roadm-Amsterdam-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 11,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 12,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L2",
+                  "link-tp-id": "roadm-Amsterdam-L2",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 13,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 14,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 15,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L2-booster",
+                  "link-tp-id": "roadm-Amsterdam-L2-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 16,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 17,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 18,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Amsterdam-Cologne",
+                  "link-tp-id": "fiber-Amsterdam-Cologne"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 19,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 20,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L1-preamp",
+                  "link-tp-id": "roadm-Cologne-L1-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 21,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 22,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 23,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L1",
+                  "link-tp-id": "roadm-Cologne-L1",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 24,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 25,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 26,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Cologne-L1)-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))",
+                  "link-tp-id": "splice-(roadm-Cologne-L1)-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 27,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 28,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Cologne-L1)-(roadm-Cologne-L2)",
+                  "link-tp-id": "patch-(roadm-Cologne-L1)-(roadm-Cologne-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 29,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 30,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))-(roadm-Cologne-L2)",
+                  "link-tp-id": "splice-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))-(roadm-Cologne-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 31,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 32,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L2",
+                  "link-tp-id": "roadm-Cologne-L2",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 33,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 34,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 35,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L2-booster",
+                  "link-tp-id": "roadm-Cologne-L2-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 36,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 37,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 38,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Cologne-Bremen",
+                  "link-tp-id": "fiber-Cologne-Bremen"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 39,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 40,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L1-preamp",
+                  "link-tp-id": "roadm-Bremen-L1-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 41,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 42,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 43,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L1",
+                  "link-tp-id": "roadm-Bremen-L1",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 44,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 45,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 46,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Bremen-L1)-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))",
+                  "link-tp-id": "splice-(roadm-Bremen-L1)-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 47,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 48,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Bremen-L1)-(roadm-Bremen-AD)",
+                  "link-tp-id": "patch-(roadm-Bremen-L1)-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 49,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 50,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))-(roadm-Bremen-AD)",
+                  "link-tp-id": "splice-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 51,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 52,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-AD",
+                  "link-tp-id": "roadm-Bremen-AD",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 53,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 54,
+                "target-channel-power": {
+                  "value": -25
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 55,
+                "num-unnum-hop": {
+                  "node-id": "trx-Bremen",
+                  "link-tp-id": "trx-Bremen",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 56,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 57,
+                "transponder": {
+                  "transponder-type": "Voyager",
+                  "transponder-mode": "mode 1"
+                }
+              }
+            }
+          ]
+        }
+      },
+      {
+        "response-id": "second",
+        "path-properties": {
+          "path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 23.46
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 23.47
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "z-a-path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 23.46
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 23.47
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "path-route-objects": [
+            {
+              "path-route-object": {
+                "index": 0,
+                "num-unnum-hop": {
+                  "node-id": "trx-Amsterdam",
+                  "link-tp-id": "trx-Amsterdam",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 1,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 2,
+                "transponder": {
+                  "transponder-type": "Voyager",
+                  "transponder-mode": "mode 1"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 3,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-AD",
+                  "link-tp-id": "roadm-Amsterdam-AD",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 4,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 5,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 6,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))",
+                  "link-tp-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 7,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 8,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1)",
+                  "link-tp-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 9,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 10,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))-(roadm-Amsterdam-L1)",
+                  "link-tp-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))-(roadm-Amsterdam-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 11,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 12,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L1",
+                  "link-tp-id": "roadm-Amsterdam-L1",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 13,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 14,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 15,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L1-booster",
+                  "link-tp-id": "roadm-Amsterdam-L1-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 16,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 17,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 18,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Amsterdam-Bremen",
+                  "link-tp-id": "fiber-Amsterdam-Bremen"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 19,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 20,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L2-preamp",
+                  "link-tp-id": "roadm-Bremen-L2-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 21,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 22,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 23,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L2",
+                  "link-tp-id": "roadm-Bremen-L2",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 24,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 25,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 26,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Bremen-L2)-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))",
+                  "link-tp-id": "splice-(roadm-Bremen-L2)-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 27,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 28,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Bremen-L2)-(roadm-Bremen-AD)",
+                  "link-tp-id": "patch-(roadm-Bremen-L2)-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 29,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 30,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))-(roadm-Bremen-AD)",
+                  "link-tp-id": "splice-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 31,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 32,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-AD",
+                  "link-tp-id": "roadm-Bremen-AD",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 33,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 34,
+                "target-channel-power": {
+                  "value": -25
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 35,
+                "num-unnum-hop": {
+                  "node-id": "trx-Bremen",
+                  "link-tp-id": "trx-Bremen",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 36,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 37,
+                "transponder": {
+                  "transponder-type": "Voyager",
+                  "transponder-mode": "mode 1"
+                }
+              }
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
diff --git a/apps/odtn/service/src/test/java/org/onosproject/odtn/impl/GnpyManagerTest.java b/apps/odtn/service/src/test/java/org/onosproject/odtn/impl/GnpyManagerTest.java
new file mode 100644
index 0000000..438491e
--- /dev/null
+++ b/apps/odtn/service/src/test/java/org/onosproject/odtn/impl/GnpyManagerTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.odtn.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Link;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.Path;
+import org.onosproject.net.link.LinkServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.TestStorageService;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Test for parsing gNPY json files.
+ */
+public class GnpyManagerTest {
+
+    private static final String REQUEST = "{\"path-request\":[{\"request-id\":\"onos-0\"," +
+            "\"source\":\"netconf:10.0.254.93:830\"," +
+            "\"destination\":\"netconf:10.0.254.94:830\",\"src-tp-id\":" +
+            "\"netconf:10.0.254.93:830\",\"dst-tp-id\":" +
+            "\"netconf:10.0.254.94:830\",\"bidirectional\":true," +
+            "\"path-constraints\":{\"te-bandwidth\":" +
+            "{\"technology\":\"flexi-grid\",\"trx_type\":\"Cassini\"," +
+            "\"trx_mode\":null,\"effective-freq-slot\":" +
+            "[{\"N\":\"null\",\"M\":\"null\"}],\"spacing\":5.0E10," +
+            "\"max-nb-of-channel\":null,\"output-power\":null," +
+            "\"path_bandwidth\":1.0E11}}}]}";
+
+    private ConnectPoint tx1 = ConnectPoint.fromString("netconf:10.0.254.93:830/1");
+    private ConnectPoint rdm1tx1 = ConnectPoint.fromString("netconf:10.0.254.107:830/1");
+    private ConnectPoint rdm1ln1 = ConnectPoint.fromString("netconf:10.0.254.107:830/2");
+    private ConnectPoint ln1rdm1 = ConnectPoint.fromString("netconf:10.0.254.101:830/1");
+    private ConnectPoint ln1ln2 = ConnectPoint.fromString("netconf:10.0.254.101:830/2");
+    private ConnectPoint ln2ln1 = ConnectPoint.fromString("netconf:10.0.254.102:830/1");
+    private ConnectPoint ln2rdm2 = ConnectPoint.fromString("netconf:10.0.254.102:830/2");
+    private ConnectPoint rdm2ln2 = ConnectPoint.fromString("netconf:10.0.254.225:830/1");
+    private ConnectPoint rdm2tx2 = ConnectPoint.fromString("netconf:10.0.254.225:830/2");
+    private ConnectPoint tx2 = ConnectPoint.fromString("netconf:10.0.254.94:830/1");
+    private Link tx1rdm1Link = DefaultLink.builder().type(Link.Type.OPTICAL)
+            .providerId(ProviderId.NONE).src(rdm1tx1).dst(tx1).build();
+    private Link rmd1ln1Link = DefaultLink.builder().type(Link.Type.OPTICAL)
+            .providerId(ProviderId.NONE).src(ln1rdm1).dst(rdm1ln1).build();
+    private Link ln1ln2Link = DefaultLink.builder().type(Link.Type.OPTICAL)
+            .providerId(ProviderId.NONE).src(ln2ln1).dst(ln1ln2).build();
+    private Link ln2rdm2Link = DefaultLink.builder().type(Link.Type.OPTICAL)
+            .providerId(ProviderId.NONE).src(rdm2ln2).dst(ln2rdm2).build();
+    private Link tx2rmd2Link = DefaultLink.builder().type(Link.Type.OPTICAL)
+            .providerId(ProviderId.NONE).src(tx2).dst(rdm2tx2).build();
+
+    private GnpyManager manager;
+    private JsonNode reply;
+
+    @Before
+    public void setUp() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        reply = mapper.readTree(this.getClass().getResourceAsStream("gnpy-response.json"));
+        manager = new GnpyManager();
+        manager.storageService = new TestStorageService();
+        manager.linkService = new InternalLinkService();
+        manager.coreService = new InternalCoreService();
+        manager.activate();
+    }
+
+    @Test
+    public void testCreateSuggestedPath() throws IOException {
+        Map<DeviceId, Double> deviceAtoBPowerMap = new HashMap<>();
+        Map<DeviceId, Double> deviceBtoAPowerMap = new HashMap<>();
+        List<DeviceId> deviceIds = manager.getDeviceAndPopulatePowerMap(reply, deviceAtoBPowerMap,
+                                                                        deviceBtoAPowerMap, "second");
+        Path path = manager.createSuggestedPath(deviceIds);
+        assertTrue(path.links().contains(tx1rdm1Link));
+        assertTrue(path.links().contains(rmd1ln1Link));
+        assertTrue(path.links().contains(ln1ln2Link));
+        assertTrue(path.links().contains(ln2rdm2Link));
+        assertTrue(path.links().contains(tx2rmd2Link));
+        assertEquals(path.src(), tx2);
+        assertEquals(path.dst(), tx1);
+
+    }
+
+    @Test
+    public void testgetDevicePowerMap() throws IOException {
+        Map<DeviceId, Double> deviceAtoBPowerMap = new HashMap<>();
+        Map<DeviceId, Double> deviceBtoAPowerMap = new HashMap<>();
+        manager.getDeviceAndPopulatePowerMap(reply, deviceAtoBPowerMap, deviceBtoAPowerMap, "second");
+        assertEquals(-25.0, deviceAtoBPowerMap.get(DeviceId.deviceId("netconf:10.0.254.107:830")));
+        assertEquals(-12.0, deviceAtoBPowerMap.get(DeviceId.deviceId("netconf:10.0.254.225:830")));
+        assertEquals(-12.0, deviceBtoAPowerMap.get(DeviceId.deviceId("netconf:10.0.254.225:830")));
+        assertEquals(-25.0, deviceBtoAPowerMap.get(DeviceId.deviceId("netconf:10.0.254.107:830")));
+    }
+
+    @Test
+    public void testGetLaunchPower() throws IOException {
+        double power = manager.getLaunchPower(reply);
+        assertEquals(0.0, power);
+    }
+
+    @Test
+    public void testGetPerHopPower() throws IOException {
+        JsonNode response = reply.get("result").get("response");
+        //getting the a-b path.
+        JsonNode responseObj = response.elements()
+                .next();
+        Iterator<JsonNode> elements = responseObj.get("path-properties")
+                .get("path-route-objects").elements();
+        Iterable<JsonNode> iterable = () -> elements;
+        List<JsonNode> elementsList = StreamSupport
+                .stream(iterable.spliterator(), false)
+                .collect(Collectors.toList());
+        double power = manager.getPerHopPower(elementsList.get(5));
+        assertEquals(-12.0, power);
+    }
+
+    @Test
+    public void testGetOsnr() throws IOException {
+        double osnr = manager.getOsnr(reply, "second");
+        assertEquals(23.47, osnr);
+    }
+
+    @Test
+    public void testCreateOchSignal() throws IOException {
+        OchSignal signal = manager.createOchSignal(reply);
+        System.out.println(signal);
+        assertEquals(signal.gridType(), GridType.DWDM);
+        assertEquals(signal.slotWidth().asGHz(), 50.000);
+        assertEquals(-35, signal.spacingMultiplier());
+    }
+
+    @Test
+    public void testCreateGnpyRequest() {
+        ConnectPoint ingress = ConnectPoint.fromString("netconf:10.0.254.93:830/1");
+        ConnectPoint egress = ConnectPoint.fromString("netconf:10.0.254.94:830/1");
+        String output = manager.createGnpyRequest(ingress, egress, true).toString();
+        System.out.println(output);
+        assertEquals("Json to create network connectivity is wrong", REQUEST, output);
+    }
+
+    private class InternalLinkService extends LinkServiceAdapter {
+        @Override
+        public Set<Link> getDeviceLinks(DeviceId deviceId) {
+            if (deviceId.equals(DeviceId.deviceId("netconf:10.0.254.94:830"))) {
+                return ImmutableSet.of(tx2rmd2Link);
+            } else if (deviceId.equals(DeviceId.deviceId("netconf:10.0.254.107:830"))) {
+                return ImmutableSet.of(tx1rdm1Link);
+            } else if (deviceId.equals(DeviceId.deviceId("netconf:10.0.254.101:830"))) {
+                return ImmutableSet.of(rmd1ln1Link);
+            } else if (deviceId.equals(DeviceId.deviceId("netconf:10.0.254.102:830"))) {
+                return ImmutableSet.of(ln1ln2Link);
+            } else if (deviceId.equals(DeviceId.deviceId("netconf:10.0.254.225:830"))) {
+                return ImmutableSet.of(ln2rdm2Link);
+            }
+            return ImmutableSet.of();
+        }
+    }
+
+    private class InternalCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId getAppId(String name) {
+            return new DefaultApplicationId(1, name);
+        }
+    }
+//    private class InternalStoreService extends StorageServiceAdapter {
+//        @Override
+//        public AtomicCounterBuilder atomicCounterBuilder() {
+//            return TestAtomicCounter.builder();
+//        }
+//    }
+}
diff --git a/apps/odtn/service/src/test/resources/org/onosproject/odtn/impl/gnpy-response.json b/apps/odtn/service/src/test/resources/org/onosproject/odtn/impl/gnpy-response.json
new file mode 100644
index 0000000..72a1273
--- /dev/null
+++ b/apps/odtn/service/src/test/resources/org/onosproject/odtn/impl/gnpy-response.json
@@ -0,0 +1,1900 @@
+{
+  "result": {
+    "response": [
+      {
+        "response-id": "first",
+        "path-properties": {
+          "path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "z-a-path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 16.92
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 21.0
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "path-route-objects": [
+            {
+              "path-route-object": {
+                "index": 0,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.93:830",
+                  "link-tp-id": "netconf:10.0.254.93:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 1,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 2,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 3,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.107:830",
+                  "link-tp-id": "netconf:10.0.254.107:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 4,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 5,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 6,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))",
+                  "link-tp-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 7,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 8,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2)",
+                  "link-tp-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 9,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 10,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))-(roadm-Amsterdam-L2)",
+                  "link-tp-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L2))-(roadm-Amsterdam-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 11,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 12,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.235:830",
+                  "link-tp-id": "netconf:10.0.254.235:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 13,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 14,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 15,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L2-booster",
+                  "link-tp-id": "roadm-Amsterdam-L2-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 16,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 17,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 18,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Amsterdam-Cologne",
+                  "link-tp-id": "fiber-Amsterdam-Cologne"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 19,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 20,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L1-preamp",
+                  "link-tp-id": "roadm-Cologne-L1-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 21,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 22,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 23,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.99:830",
+                  "link-tp-id": "netconf:10.0.254.99:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 24,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 25,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 26,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Cologne-L1)-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))",
+                  "link-tp-id": "splice-(roadm-Cologne-L1)-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 27,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 28,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Cologne-L1)-(roadm-Cologne-L2)",
+                  "link-tp-id": "patch-(roadm-Cologne-L1)-(roadm-Cologne-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 29,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 30,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))-(roadm-Cologne-L2)",
+                  "link-tp-id": "splice-(patch-(roadm-Cologne-L1)-(roadm-Cologne-L2))-(roadm-Cologne-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 31,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 32,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.104:830",
+                  "link-tp-id": "netconf:10.0.254.104:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 33,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 34,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 35,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L2-booster",
+                  "link-tp-id": "roadm-Cologne-L2-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 36,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 37,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 38,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Cologne-Bremen",
+                  "link-tp-id": "fiber-Cologne-Bremen"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 39,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 40,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L1-preamp",
+                  "link-tp-id": "roadm-Bremen-L1-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 41,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 42,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 43,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.100:830",
+                  "link-tp-id": "netconf:10.0.254.100:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 44,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 45,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 46,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Bremen-L1)-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))",
+                  "link-tp-id": "splice-(roadm-Bremen-L1)-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 47,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 48,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Bremen-L1)-(roadm-Bremen-AD)",
+                  "link-tp-id": "patch-(roadm-Bremen-L1)-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 49,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 50,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))-(roadm-Bremen-AD)",
+                  "link-tp-id": "splice-(patch-(roadm-Bremen-L1)-(roadm-Bremen-AD))-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 51,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 52,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.225:830",
+                  "link-tp-id": "netconf:10.0.254.225:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 53,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 54,
+                "target-channel-power": {
+                  "value": -25
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 55,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.94:830",
+                  "link-tp-id": "netconf:10.0.254.94:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 56,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 57,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            }
+          ],
+          "reversed-path-route-objects": [
+            {
+              "path-route-object": {
+                "index": 0,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.94:830",
+                  "link-tp-id": "netconf:10.0.254.94:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 1,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 2,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 3,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.225:830",
+                  "link-tp-id": "netconf:10.0.254.225:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 4,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 5,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 6,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Bremen-AD)-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L1))",
+                  "link-tp-id": "splice-(roadm-Bremen-AD)-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L1))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 7,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 8,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Bremen-AD)-(roadm-Bremen-L1)",
+                  "link-tp-id": "patch-(roadm-Bremen-AD)-(roadm-Bremen-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 9,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 10,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L1))-(roadm-Bremen-L1)",
+                  "link-tp-id": "splice-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L1))-(roadm-Bremen-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 11,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 12,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.100:830",
+                  "link-tp-id": "netconf:10.0.254.100:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 13,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 14,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 15,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L1-booster",
+                  "link-tp-id": "roadm-Bremen-L1-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 16,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 17,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 18,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Bremen-Cologne",
+                  "link-tp-id": "fiber-Bremen-Cologne"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 19,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 20,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L2-preamp",
+                  "link-tp-id": "roadm-Cologne-L2-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 21,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 22,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 23,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.104:830",
+                  "link-tp-id": "netconf:10.0.254.104:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 24,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 25,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 26,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Cologne-L2)-(patch-(roadm-Cologne-L2)-(roadm-Cologne-L1))",
+                  "link-tp-id": "splice-(roadm-Cologne-L2)-(patch-(roadm-Cologne-L2)-(roadm-Cologne-L1))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 27,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 28,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Cologne-L2)-(roadm-Cologne-L1)",
+                  "link-tp-id": "patch-(roadm-Cologne-L2)-(roadm-Cologne-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 29,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 30,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Cologne-L2)-(roadm-Cologne-L1))-(roadm-Cologne-L1)",
+                  "link-tp-id": "splice-(patch-(roadm-Cologne-L2)-(roadm-Cologne-L1))-(roadm-Cologne-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 31,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 32,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.99:830",
+                  "link-tp-id": "netconf:10.0.254.99:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 33,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 34,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 35,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Cologne-L1-booster",
+                  "link-tp-id": "roadm-Cologne-L1-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 36,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 37,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 38,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Cologne-Amsterdam",
+                  "link-tp-id": "fiber-Cologne-Amsterdam"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 39,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 40,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L2-preamp",
+                  "link-tp-id": "roadm-Amsterdam-L2-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 41,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 42,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 43,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.235:830",
+                  "link-tp-id": "netconf:10.0.254.235:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 44,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 45,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 46,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Amsterdam-L2)-(patch-(roadm-Amsterdam-L2)-(roadm-Amsterdam-AD))",
+                  "link-tp-id": "splice-(roadm-Amsterdam-L2)-(patch-(roadm-Amsterdam-L2)-(roadm-Amsterdam-AD))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 47,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 48,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Amsterdam-L2)-(roadm-Amsterdam-AD)",
+                  "link-tp-id": "patch-(roadm-Amsterdam-L2)-(roadm-Amsterdam-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 49,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 50,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Amsterdam-L2)-(roadm-Amsterdam-AD))-(roadm-Amsterdam-AD)",
+                  "link-tp-id": "splice-(patch-(roadm-Amsterdam-L2)-(roadm-Amsterdam-AD))-(roadm-Amsterdam-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 51,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 52,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.107:830",
+                  "link-tp-id": "netconf:10.0.254.107:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 53,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 54,
+                "target-channel-power": {
+                  "value": -25
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 55,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.93:830",
+                  "link-tp-id": "netconf:10.0.254.93:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 56,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 57,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            }
+          ]
+        }
+      },
+      {
+        "response-id": "second",
+        "path-properties": {
+          "path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 23.46
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 23.47
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "z-a-path-metric": [
+            {
+              "metric-type": "SNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "SNR-0.1nm",
+              "accumulative-value": 23.46
+            },
+            {
+              "metric-type": "OSNR-bandwidth",
+              "accumulative-value": 19.38
+            },
+            {
+              "metric-type": "OSNR-0.1nm",
+              "accumulative-value": 23.47
+            },
+            {
+              "metric-type": "reference_power",
+              "accumulative-value": 0.001
+            },
+            {
+              "metric-type": "path_bandwidth",
+              "accumulative-value": 100000000000.0
+            }
+          ],
+          "path-route-objects": [
+            {
+              "path-route-object": {
+                "index": 0,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.93:830",
+                  "link-tp-id": "netconf:10.0.254.93:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 1,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 2,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 3,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.107:830",
+                  "link-tp-id": "netconf:10.0.254.107:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 4,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 5,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 6,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))",
+                  "link-tp-id": "splice-(roadm-Amsterdam-AD)-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 7,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 8,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1)",
+                  "link-tp-id": "patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 9,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 10,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))-(roadm-Amsterdam-L1)",
+                  "link-tp-id": "splice-(patch-(roadm-Amsterdam-AD)-(roadm-Amsterdam-L1))-(roadm-Amsterdam-L1)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 11,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 12,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.101:830",
+                  "link-tp-id": "netconf:10.0.254.101:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 13,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 14,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 15,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L1-booster",
+                  "link-tp-id": "roadm-Amsterdam-L1-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 16,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 17,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 18,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Amsterdam-Bremen",
+                  "link-tp-id": "fiber-Amsterdam-Bremen"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 19,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 20,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L2-preamp",
+                  "link-tp-id": "roadm-Bremen-L2-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 21,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 22,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 23,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.102:830",
+                  "link-tp-id": "netconf:10.0.254.102:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 24,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 25,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 26,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Bremen-L2)-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))",
+                  "link-tp-id": "splice-(roadm-Bremen-L2)-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 27,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 28,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Bremen-L2)-(roadm-Bremen-AD)",
+                  "link-tp-id": "patch-(roadm-Bremen-L2)-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 29,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 30,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))-(roadm-Bremen-AD)",
+                  "link-tp-id": "splice-(patch-(roadm-Bremen-L2)-(roadm-Bremen-AD))-(roadm-Bremen-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 31,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 32,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.225:830",
+                  "link-tp-id": "netconf:10.0.254.225:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 33,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 34,
+                "target-channel-power": {
+                  "value": -25
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 35,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.94:830",
+                  "link-tp-id": "netconf:10.0.254.94:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 36,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 37,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            }
+          ],
+          "reversed-path-route-objects": [
+            {
+              "path-route-object": {
+                "index": 0,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.94:830",
+                  "link-tp-id": "netconf:10.0.254.94:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 1,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 2,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 3,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.225:830",
+                  "link-tp-id": "netconf:10.0.254.225:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 4,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 5,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 6,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Bremen-AD)-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L2))",
+                  "link-tp-id": "splice-(roadm-Bremen-AD)-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L2))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 7,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 8,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Bremen-AD)-(roadm-Bremen-L2)",
+                  "link-tp-id": "patch-(roadm-Bremen-AD)-(roadm-Bremen-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 9,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 10,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L2))-(roadm-Bremen-L2)",
+                  "link-tp-id": "splice-(patch-(roadm-Bremen-AD)-(roadm-Bremen-L2))-(roadm-Bremen-L2)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 11,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 12,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.102:830",
+                  "link-tp-id": "netconf:10.0.254.102:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 13,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 14,
+                "target-channel-power": {
+                  "value": -23
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 15,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Bremen-L2-booster",
+                  "link-tp-id": "roadm-Bremen-L2-booster",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 16,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 17,
+                "target-channel-power": {
+                  "value": -1
+                },
+                "output-voa": {
+                  "value": 12.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 18,
+                "num-unnum-hop": {
+                  "node-id": "fiber-Bremen-Amsterdam",
+                  "link-tp-id": "fiber-Bremen-Amsterdam"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 19,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 20,
+                "num-unnum-hop": {
+                  "node-id": "roadm-Amsterdam-L1-preamp",
+                  "link-tp-id": "roadm-Amsterdam-L1-preamp",
+                  "gnpy-node-type": "EDFA"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 21,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 22,
+                "target-channel-power": {
+                  "value": 1.0
+                },
+                "output-voa": {
+                  "value": 0.0
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 23,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.101:830",
+                  "link-tp-id": "netconf:10.0.254.101:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 24,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 25,
+                "target-channel-power": {
+                  "value": -12
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 26,
+                "num-unnum-hop": {
+                  "node-id": "splice-(roadm-Amsterdam-L1)-(patch-(roadm-Amsterdam-L1)-(roadm-Amsterdam-AD))",
+                  "link-tp-id": "splice-(roadm-Amsterdam-L1)-(patch-(roadm-Amsterdam-L1)-(roadm-Amsterdam-AD))"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 27,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 28,
+                "num-unnum-hop": {
+                  "node-id": "patch-(roadm-Amsterdam-L1)-(roadm-Amsterdam-AD)",
+                  "link-tp-id": "patch-(roadm-Amsterdam-L1)-(roadm-Amsterdam-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 29,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 30,
+                "num-unnum-hop": {
+                  "node-id": "splice-(patch-(roadm-Amsterdam-L1)-(roadm-Amsterdam-AD))-(roadm-Amsterdam-AD)",
+                  "link-tp-id": "splice-(patch-(roadm-Amsterdam-L1)-(roadm-Amsterdam-AD))-(roadm-Amsterdam-AD)"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 31,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 32,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.107:830",
+                  "link-tp-id": "netconf:10.0.254.107:830",
+                  "gnpy-node-type": "ROADM"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 33,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 34,
+                "target-channel-power": {
+                  "value": -25
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 35,
+                "num-unnum-hop": {
+                  "node-id": "netconf:10.0.254.93:830",
+                  "link-tp-id": "netconf:10.0.254.93:830",
+                  "gnpy-node-type": "transceiver"
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 36,
+                "label-hop": {
+                  "N": -284,
+                  "M": 4
+                }
+              }
+            },
+            {
+              "path-route-object": {
+                "index": 37,
+                "transponder": {
+                  "transponder-type": "Cassini",
+                  "transponder-mode": "dp-qpsk"
+                }
+              }
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
index 833b20d..1564167 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
@@ -145,7 +145,7 @@
         ConnectPoint egress = createConnectPoint(egressString);
 
         Intent intent = createOpticalIntent(ingress, egress, deviceService,
-                key(), appId(), bidirectional, createOchSignal());
+                key(), appId(), bidirectional, createOchSignal(), null);
 
         service.submit(intent);
         print("Optical intent submitted:\n%s", intent.toString());
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java
index 8aea7c9..53c6cec 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java
@@ -61,12 +61,13 @@
      * @param bidirectional if this argument is true, the optical link created
      * will be bidirectional, otherwise the link will be unidirectional.
      * @param signal optical signal
+     * @param suggestedPath suggested path
      *
      * @return created intent
      */
     public static Intent createOpticalIntent(ConnectPoint ingress, ConnectPoint
             egress, DeviceService deviceService, Key key, ApplicationId appId, boolean
-            bidirectional, OchSignal signal) {
+            bidirectional, OchSignal signal, Path suggestedPath) {
 
         Intent intent = null;
 
@@ -125,6 +126,7 @@
                     .signalType(signalType)
                     .bidirectional(bidirectional)
                     .ochSignal(signal)
+                    .suggestedPath(suggestedPath)
                     .build();
         } else {
             log.debug("Unable to create optical intent between connect points {} and {}", ingress, egress);