[ONOS-7839] suggestedPath for optical connectivity intent with optical-rest support

Change-Id: I2b5093ac26149e450a14467c71656447233b5ce2
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
index 5a76aef..1355337 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -60,13 +60,13 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -139,8 +139,15 @@
         resources.add(srcPortResource);
         resources.add(dstPortResource);
 
+        // If there is a suggestedPath, use this path without further checking, otherwise trigger path computation
+        Stream<Path> paths;
+        if (intent.suggestedPath().isPresent()) {
+            paths = Stream.of(intent.suggestedPath().get());
+        } else {
+            paths = getOpticalPaths(intent);
+        }
+
         // Find first path that has the required resources
-        Stream<Path> paths = getOpticalPaths(intent);
         Optional<Map.Entry<Path, List<OchSignal>>> found = paths
                 .map(path -> Maps.immutableEntry(path, findFirstAvailableLambda(intent, path)))
                 .filter(entry -> !entry.getValue().isEmpty())
@@ -350,8 +357,16 @@
                 return ScalarWeight.NON_VIABLE_WEIGHT;
             }
 
+            /**
+             *
+             * @param edge edge to be weighed
+             * @return the metric retrieved from the annotations otherwise 1
+             */
             @Override
             public Weight weight(TopologyEdge edge) {
+
+                log.debug("Link {} metric {}", edge.link(), edge.link().annotations().value("metric"));
+
                 // Disregard inactive or non-optical links
                 if (edge.link().state() == Link.State.INACTIVE) {
                     return ScalarWeight.toWeight(-1);
@@ -375,7 +390,13 @@
                     }
                 }
 
-                return ScalarWeight.toWeight(1);
+                String metricString = edge.link().annotations().value("metric");
+                if (!metricString.isEmpty()) {
+                    double metric = Double.parseDouble(metricString);
+                    return ScalarWeight.toWeight(metric);
+                } else {
+                    return ScalarWeight.toWeight(1);
+                }
             }
         };
 
@@ -418,6 +439,7 @@
                         return path;
                     });
         }
+
         return paths;
     }
 }
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 8deb5ba..004c302 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
@@ -25,6 +25,7 @@
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OduSignalType;
 import org.onosproject.net.Port;
+import org.onosproject.net.Path;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.Key;
@@ -84,7 +85,7 @@
 
             // continue only if both OduClt port's Devices are of the same type
             if (!(srcDevice.type().equals(dstDevice.type()))) {
-                log.debug("Devices without same deviceType: SRC=%s and DST=%s", srcDevice.type(), dstDevice.type());
+                log.debug("Devices without same deviceType: SRC {} and DST={}", srcDevice.type(), dstDevice.type());
                 return intent;
             }
 
@@ -109,7 +110,7 @@
                         .bidirectional(bidirectional)
                         .build();
             } else {
-                log.debug("Wrong Device Type for connect points %s and %s", ingress, egress);
+                log.debug("Wrong Device Type for connect points {} and {}", ingress, egress);
             }
         } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
             OduSignalType signalType = ((OchPort) srcPort).signalType();
@@ -123,7 +124,92 @@
                     .ochSignal(signal)
                     .build();
         } else {
-            log.debug("Unable to create optical intent between connect points %s and %s", ingress, egress);
+            log.debug("Unable to create optical intent between connect points {} and {}", ingress, egress);
+        }
+
+        return intent;
+    }
+
+    /**
+     * Returns a new optical intent created from the method parameters, strict suggestedPath is specified.
+     *
+     * @param ingress ingress description (device/port)
+     * @param egress egress description (device/port)
+     * @param deviceService device service
+     * @param key intent key
+     * @param appId application id
+     * @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 path suggested path for the intent
+     *
+     * @return created intent
+     */
+    public static Intent createExplicitOpticalIntent(ConnectPoint ingress, ConnectPoint
+            egress, DeviceService deviceService, Key key, ApplicationId appId, boolean
+                                                     bidirectional, OchSignal signal, Path path) {
+
+        Intent intent = null;
+
+        if (ingress == null || egress == null) {
+            log.error("Invalid endpoint(s); could not create optical intent");
+            return intent;
+        }
+
+        DeviceService ds = opticalView(deviceService);
+
+        Port srcPort = ds.getPort(ingress.deviceId(), ingress.port());
+        Port dstPort = ds.getPort(egress.deviceId(), egress.port());
+
+        if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
+            Device srcDevice = ds.getDevice(ingress.deviceId());
+            Device dstDevice = ds.getDevice(egress.deviceId());
+
+            // continue only if both OduClt port's Devices are of the same type
+            if (!(srcDevice.type().equals(dstDevice.type()))) {
+                log.debug("Devices without same deviceType: SRC={} and DST={}", srcDevice.type(), dstDevice.type());
+                return intent;
+            }
+
+            CltSignalType signalType = ((OduCltPort) srcPort).signalType();
+            if (Device.Type.ROADM.equals(srcDevice.type()) ||
+                    Device.Type.ROADM_OTN.equals(srcDevice.type())) {
+                intent = OpticalCircuitIntent.builder()
+                        .appId(appId)
+                        .key(key)
+                        .src(ingress)
+                        .dst(egress)
+                        .signalType(signalType)
+                        .bidirectional(bidirectional)
+                        .build();
+            } else if (Device.Type.OTN.equals(srcDevice.type())) {
+                intent = OpticalOduIntent.builder()
+                        .appId(appId)
+                        .key(key)
+                        .src(ingress)
+                        .dst(egress)
+                        .signalType(signalType)
+                        .bidirectional(bidirectional)
+                        .build();
+            } else {
+                log.error("Wrong Device Type for connect points: " +
+                        "ingress {} of type {}; egress {} of type {}",
+                        ingress, srcDevice.type(), egress, dstDevice.type());
+            }
+        } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
+            OduSignalType signalType = ((OchPort) srcPort).signalType();
+            intent = OpticalConnectivityIntent.builder()
+                    .appId(appId)
+                    .key(key)
+                    .src(ingress)
+                    .dst(egress)
+                    .signalType(signalType)
+                    .bidirectional(bidirectional)
+                    .ochSignal(signal)
+                    .suggestedPath(path)
+                    .build();
+        } else {
+            log.error("Unable to create explicit optical intent between connect points {} and {}", ingress, egress);
         }
 
         return intent;
diff --git a/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java b/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
index 2d16e02..2cb1143 100644
--- a/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
+++ b/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
@@ -20,23 +20,40 @@
 import static org.onlab.util.Tools.nullIsNotFound;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.graph.ScalarWeight;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.OchSignal;
+import org.onosproject.net.Device;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Link;
+import org.onosproject.net.DefaultPath;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.link.LinkService;
 import org.onosproject.net.optical.json.OchSignalCodec;
+import org.onosproject.net.provider.ProviderId;
 import org.onosproject.rest.AbstractWebResource;
 import org.slf4j.Logger;
 
-import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
+import javax.ws.rs.GET;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Consumes;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -44,11 +61,15 @@
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
 
+import static org.onosproject.net.optical.util.OpticalIntentUtility.createExplicitOpticalIntent;
 import static org.slf4j.LoggerFactory.getLogger;
 
 import static org.onlab.util.Tools.readTreeFromStream;
-import static org.onosproject.net.optical.util.OpticalIntentUtility.createOpticalIntent;
 
 
 /**
@@ -60,20 +81,15 @@
     private static final Logger log = getLogger(OpticalIntentsWebResource.class);
 
     private static final String JSON_INVALID = "Invalid json input";
-
     private static final String APP_ID = "appId";
-
     private static final String INGRESS_POINT = "ingressPoint";
     private static final String EGRESS_POINT = "egressPoint";
-
     private static final String BIDIRECTIONAL = "bidirectional";
-
     private static final String SIGNAL = "signal";
-
-    protected static final String MISSING_MEMBER_MESSAGE =
-            " member is required";
-    private static final String E_APP_ID_NOT_FOUND =
-            "Application ID is not found";
+    private static final String SUGGESTEDPATH = "suggestedPath";
+    private static final String MISSING_MEMBER_MESSAGE = " member is required";
+    private static final String E_APP_ID_NOT_FOUND = "Application ID is not found";
+    private static final ProviderId PROVIDER_ID = new ProviderId("netconf", "optical-rest");
 
     @Context
     private UriInfo uriInfo;
@@ -108,6 +124,130 @@
         }
     }
 
+    /**
+     * Get the optical intents on the network.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getIntents() {
+
+        DeviceService deviceService = get(DeviceService.class);
+
+        IntentService intentService = get(IntentService.class);
+        Iterator intentItr = intentService.getIntents().iterator();
+
+        ArrayNode arrayFlows = mapper().createArrayNode();
+
+        while (intentItr.hasNext()) {
+
+            Intent intent = (Intent) intentItr.next();
+            if (intent instanceof OpticalConnectivityIntent) {
+
+                OpticalConnectivityIntent opticalConnectivityIntent = (OpticalConnectivityIntent) intent;
+
+                Device srcDevice = deviceService.getDevice(opticalConnectivityIntent.getSrc().deviceId());
+                Device dstDevice = deviceService.getDevice(opticalConnectivityIntent.getDst().deviceId());
+
+                String srcDeviceName = srcDevice.annotations().value(AnnotationKeys.NAME);
+                String dstDeviceName = dstDevice.annotations().value(AnnotationKeys.NAME);
+
+                ObjectNode objectNode = mapper().createObjectNode();
+
+                objectNode.put("intent id", opticalConnectivityIntent.id().toString());
+                objectNode.put("app id", opticalConnectivityIntent.appId().name());
+                objectNode.put("state", intentService.getIntentState(opticalConnectivityIntent.key()).toString());
+                objectNode.put("src", opticalConnectivityIntent.getSrc().toString());
+                objectNode.put("dst", opticalConnectivityIntent.getDst().toString());
+                objectNode.put("srcName", srcDeviceName);
+                objectNode.put("dstName", dstDeviceName);
+
+                //Only for INSTALLED intents
+                if (intentService.getIntentState(intent.key()) == IntentState.INSTALLED) {
+
+                    //Retrieve associated FlowRuleIntent
+                    FlowRuleIntent installableIntent =
+                            (FlowRuleIntent) intentService.getInstallableIntents(opticalConnectivityIntent.key())
+                            .stream()
+                            .filter(FlowRuleIntent.class::isInstance)
+                            .findFirst()
+                            .orElse(null);
+
+                    //Retrieve used ochSignal from the Selector of one of the installed FlowRule
+                    //TODO store utilized ochSignal in the intent resources
+                    if (installableIntent != null) {
+                        OchSignal signal = installableIntent.flowRules().stream()
+                                .map(r -> ((OchSignalCriterion)
+                                        r.selector().getCriterion(Criterion.Type.OCH_SIGID)).lambda())
+                                .findFirst()
+                                .orElse(null);
+
+                        objectNode.put("ochSignal", signal.toString());
+                        objectNode.put("centralFreq", signal.centralFrequency().asTHz() + " THz");
+                    }
+
+                    //Retrieve path and print it to REST
+                    if (installableIntent != null) {
+                        String path = installableIntent.resources().stream()
+                                .filter(Link.class::isInstance)
+                                .map(Link.class::cast)
+                                .map(r -> deviceService.getDevice(r.src().deviceId()))
+                                .map(r -> r.annotations().value(AnnotationKeys.NAME))
+                                .collect(Collectors.joining(" -> "));
+
+                        List<Link> pathLinks = installableIntent.resources().stream()
+                                .filter(Link.class::isInstance)
+                                .map(Link.class::cast)
+                                .collect(Collectors.toList());
+
+                        DefaultPath defaultPath = new DefaultPath(PROVIDER_ID, pathLinks, new ScalarWeight(1));
+
+                        objectNode.put("path", defaultPath.toString());
+                        objectNode.put("pathName", path + " -> " + dstDeviceName);
+                    }
+                }
+
+                arrayFlows.add(objectNode);
+            }
+        }
+
+        ObjectNode root = this.mapper().createObjectNode().putPOJO("Intents", arrayFlows);
+        return ok(root).build();
+    }
+
+    /**
+     * Delete the specified optical intent.
+     *
+     * @param appId application identifier
+     * @param keyString   intent key
+     * @return 204 NO CONTENT
+     */
+    @DELETE
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Path("{appId}/{key}")
+    public Response deleteIntent(@PathParam("appId") String appId,
+                                 @PathParam("key") String keyString) {
+
+        final ApplicationId app = get(CoreService.class).getAppId(appId);
+        nullIsNotFound(app, "Application Id not found");
+
+        IntentService intentService = get(IntentService.class);
+        Intent intent = intentService.getIntent(Key.of(keyString, app));
+        if (intent == null) {
+            intent = intentService.getIntent(Key.of(Long.decode(keyString), app));
+        }
+        nullIsNotFound(intent, "Intent Id is not found");
+
+        if (intent instanceof OpticalConnectivityIntent) {
+            intentService.withdraw(intent);
+        } else {
+            throw new IllegalArgumentException("Specified intent is not of type OpticalConnectivityIntent");
+        }
+
+        return Response.noContent().build();
+    }
+
     private Intent decode(ObjectNode json) {
         JsonNode ingressJson = json.get(INGRESS_POINT);
         if (!ingressJson.isObject()) {
@@ -139,9 +279,75 @@
         String appIdString = nullIsIllegal(json.get(APP_ID), APP_ID + MISSING_MEMBER_MESSAGE).asText();
         CoreService service = getService(CoreService.class);
         ApplicationId appId = nullIsNotFound(service.getAppId(appIdString), E_APP_ID_NOT_FOUND);
+
         Key key = null;
         DeviceService deviceService = get(DeviceService.class);
 
-        return createOpticalIntent(ingress, egress, deviceService, key, appId, bidirectional, signal);
+        JsonNode suggestedPathJson = json.get(SUGGESTEDPATH);
+        DefaultPath suggestedPath = null;
+        LinkService linkService = get(LinkService.class);
+
+        if (suggestedPathJson != null) {
+            if (!suggestedPathJson.isObject()) {
+                throw new IllegalArgumentException(JSON_INVALID);
+            } else {
+                ArrayNode linksJson = nullIsIllegal((ArrayNode) suggestedPathJson.get("links"),
+                        "Suggested path specified without links");
+
+                List<Link> listLinks = new ArrayList<>();
+
+                for (JsonNode node : linksJson) {
+
+                    String srcString = node.get("src").asText();
+                    String dstString = node.get("dst").asText();
+
+                    ConnectPoint srcConnectPoint = ConnectPoint.fromString(srcString);
+                    ConnectPoint dstConnectPoint = ConnectPoint.fromString(dstString);
+
+                    Link link = linkService.getLink(srcConnectPoint, dstConnectPoint);
+                    if (link == null) {
+                        throw new IllegalArgumentException("Not existing link in the suggested path");
+                    }
+
+                    listLinks.add(link);
+                }
+
+                if ((!listLinks.get(0).src().deviceId().equals(ingress.deviceId())) ||
+                        (!listLinks.get(0).src().port().equals(ingress.port())) ||
+                        (!listLinks.get(listLinks.size() - 1).dst().deviceId().equals(egress.deviceId())) ||
+                        (!listLinks.get(listLinks.size() - 1).dst().port().equals(egress.port()))) {
+                    throw new IllegalArgumentException(
+                            "Suggested path not compatible with ingress or egress connect points");
+                }
+
+                if (!isPathContiguous(listLinks)) {
+                    throw new IllegalArgumentException(
+                            "Links specified in the suggested path are not contiguous");
+                }
+
+                suggestedPath = new DefaultPath(PROVIDER_ID, listLinks, new ScalarWeight(1));
+
+                log.debug("OpticalIntent along suggestedPath {}", suggestedPath);
+            }
+        }
+
+        return createExplicitOpticalIntent(
+                ingress, egress, deviceService, key, appId, bidirectional, signal, suggestedPath);
+    }
+
+    private boolean isPathContiguous(List<Link> path) {
+        DeviceId previousDst;
+        DeviceId currentSrc;
+
+        for (int i = 1; i < path.size(); i++) {
+            previousDst = path.get(i - 1).dst().deviceId();
+            currentSrc = path.get(i).src().deviceId();
+
+            if (!previousDst.equals(currentSrc)) {
+                log.debug("OpticalIntent links are not contiguous previous {} current {}", previousDst, currentSrc);
+                return false;
+            }
+        }
+        return true;
     }
 }
diff --git a/apps/optical-rest/src/main/resources/definitions/CreateIntent.json b/apps/optical-rest/src/main/resources/definitions/CreateIntent.json
index 5565a24..da4f215 100644
--- a/apps/optical-rest/src/main/resources/definitions/CreateIntent.json
+++ b/apps/optical-rest/src/main/resources/definitions/CreateIntent.json
@@ -8,7 +8,7 @@
   "properties": {
     "appId": {
       "type": "string",
-      "example": "org.onosproject.ovsdb"
+      "example": "org.onosproject.optical-rest"
     },
     "ingressPoint": {
       "type": "object",
@@ -20,11 +20,11 @@
       "properties": {
         "device": {
           "type": "string",
-          "example": "of:0000000000000001" 
+          "example": "netconf:10.255.255.14:2022"
         },
         "port": {
           "type": "string",
-          "example": "1" 
+          "example": "101"
         }
       }
     },
@@ -38,17 +38,17 @@
       "properties": {
         "device": {
           "type": "string",
-          "example": "of:0000000000000002"
+          "example": "netconf:10.255.255.17:2022"
         },
         "port": {
           "type": "string",
-          "example": "200"
+          "example": "201"
         }
       }
     },
     "bidirection": {
       "type": "boolean",
-      "example": true
+      "example": false
     },
     "signal": {
       "type": "object",
@@ -62,12 +62,22 @@
       "properties": {
         "channelSpacing": {
           "type": "string",
-          "enum": ["CHL_100GHZ", "CHL_50GHZ", "CHL_25GHZ", "CHL_12P5GHZ", "CHL_6P25GHZ"],
+          "enum": [
+            "CHL_100GHZ",
+            "CHL_50GHZ",
+            "CHL_25GHZ",
+            "CHL_12P5GHZ",
+            "CHL_6P25GHZ"
+          ],
           "example": "CHL_50GHZ"
         },
         "gridType": {
           "type": "string",
-          "enum": ["DWDM", "CWDM", "FLEX"],
+          "enum": [
+            "DWDM",
+            "CWDM",
+            "FLEX"
+          ],
           "example": "DWDM"
         },
         "spacingMultiplier": {
@@ -81,6 +91,40 @@
           "example": 4
         }
       }
+    },
+    "suggestedPath": {
+      "type": "object",
+      "title": "suggestedPath",
+      "required": [
+        "links"
+      ],
+      "properties": {
+        "links": {
+          "type": "array",
+          "title": "suggestedPath",
+          "required": [
+            "link"
+          ],
+          "items": {
+            "type": "object",
+            "title": "link",
+            "required": [
+              "src",
+              "dst"
+            ],
+            "properties" : {
+              "src": {
+                "type": "string",
+                "example": "netconf:10.255.255.14:2022/101"
+              },
+              "dst": {
+                "type": "string",
+                "example": "netconf:10.255.255.9:2022/201"
+              }
+            }
+          }
+        }
+      }
     }
   }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/intent/OpticalConnectivityIntent.java b/core/api/src/main/java/org/onosproject/net/intent/OpticalConnectivityIntent.java
index 7cf4411..59247e1 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/OpticalConnectivityIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/OpticalConnectivityIntent.java
@@ -22,6 +22,7 @@
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OduSignalType;
 import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.Path;
 
 import java.util.Collections;
 import java.util.Optional;
@@ -31,6 +32,7 @@
 /**
  * An optical layer intent for connectivity between two OCh ports.
  * No traffic selector or traffic treatment are needed.
+ * OchSignal and suggestedPath are optional.
  */
 @Beta
 public final class OpticalConnectivityIntent extends Intent {
@@ -38,7 +40,9 @@
     private final ConnectPoint dst;
     private final OduSignalType signalType;
     private final boolean isBidirectional;
+
     private final Optional<OchSignal> ochSignal;
+    private final Optional<Path> suggestedPath;
 
     /**
      * Creates an optical connectivity intent between the specified
@@ -51,6 +55,7 @@
      * @param signalType signal type
      * @param isBidirectional indicates if intent is unidirectional
      * @param ochSignal optional OCh signal
+     * @param suggestedPath optional suggested path
      * @param priority priority to use for flows from this intent
      * @param resourceGroup resource group of this intent
      */
@@ -61,6 +66,7 @@
                                         OduSignalType signalType,
                                         boolean isBidirectional,
                                         Optional<OchSignal> ochSignal,
+                                        Optional<Path> suggestedPath,
                                         int priority,
                                         ResourceGroup resourceGroup) {
         super(appId, key, Collections.emptyList(), priority, resourceGroup);
@@ -69,6 +75,7 @@
         this.signalType = checkNotNull(signalType);
         this.isBidirectional = isBidirectional;
         this.ochSignal = ochSignal;
+        this.suggestedPath = suggestedPath;
     }
 
     /**
@@ -90,6 +97,7 @@
         private OduSignalType signalType;
         private boolean isBidirectional;
         private Optional<OchSignal> ochSignal = Optional.empty();
+        private Optional<Path> suggestedPath = Optional.empty();
 
         @Override
         public Builder appId(ApplicationId appId) {
@@ -167,6 +175,17 @@
         }
 
         /**
+         * Sets the suggestedPath of the intent.
+         *
+         * @param suggestedPath the path
+         * @return this builder
+         */
+        public Builder suggestedPath(Path suggestedPath) {
+            this.suggestedPath = Optional.ofNullable(suggestedPath);
+            return this;
+        }
+
+        /**
          * Builds an optical connectivity intent from the accumulated parameters.
          *
          * @return point to point intent
@@ -181,6 +200,7 @@
                     signalType,
                     isBidirectional,
                     ochSignal,
+                    suggestedPath,
                     priority,
                     resourceGroup
             );
@@ -197,6 +217,7 @@
         this.signalType = null;
         this.isBidirectional = false;
         this.ochSignal = null;
+        this.suggestedPath = null;
     }
 
     /**
@@ -240,10 +261,20 @@
      *
      * @return the lambda
      */
+
     public Optional<OchSignal> ochSignal() {
         return ochSignal;
     }
 
+    /**
+     * Returns the suggestedPath of the intent.
+     *
+     * @return the suggestedPath
+     */
+    public Optional<Path> suggestedPath() {
+        return suggestedPath;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
@@ -257,6 +288,7 @@
                 .add("signalType", signalType)
                 .add("isBidirectional", isBidirectional)
                 .add("ochSignal", ochSignal)
+                .add("suggestedPath", suggestedPath)
                 .add("resourceGroup", resourceGroup())
                 .toString();
     }