Added support for optical intent between the different ports on same
device.

Currently ONOS doesn't allow to create opticalIntent between two different
ports of the same device, which makes sense for normal devices. But in case
of bigSwitch where bigSwitch is composed of many optical devices, we need
to have this functionality.

Change-Id: Ieb7c4a74d45c5d73bad04fafb66df7c6533a602d
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 e3794c2..3ad3c0e 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
@@ -25,9 +25,13 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultLink;
 import org.onosproject.net.DefaultOchSignalComparator;
+import org.onosproject.net.DefaultPath;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.GridType;
 import org.onosproject.net.Link;
@@ -42,6 +46,7 @@
 import org.onosproject.net.intent.OpticalConnectivityIntent;
 import org.onosproject.net.intent.OpticalPathIntent;
 import org.onosproject.net.optical.OchPort;
+import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.resource.Resource;
 import org.onosproject.net.resource.ResourceAllocation;
 import org.onosproject.net.resource.ResourceService;
@@ -63,8 +68,8 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import java.util.stream.IntStream;
+import java.util.stream.Stream;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
@@ -78,6 +83,8 @@
     private static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
     // By default, allocate 50 GHz lambdas (4 slots of 12.5 GHz) for each intent.
     private static final int SLOT_COUNT = 4;
+    private static final ProviderId PROVIDER_ID = new ProviderId("opticalConnectivityIntent",
+            "org.onosproject.net.optical.intent");
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentExtensionService intentManager;
@@ -137,7 +144,7 @@
                 .map(path -> Maps.immutableEntry(path, findFirstAvailableLambda(intent, path)))
                 .filter(entry -> !entry.getValue().isEmpty())
                 .filter(entry -> convertToResources(entry.getKey(),
-                                                    entry.getValue()).stream().allMatch(resourceService::isAvailable))
+                        entry.getValue()).stream().allMatch(resourceService::isAvailable))
                 .findFirst();
 
         // Allocate resources and create optical path intent
@@ -157,8 +164,8 @@
      * Only supports fixed grid for now.
      *
      * @param parentIntent this intent (used for resource tracking)
-     * @param path the path to use
-     * @param lambda the lambda to use
+     * @param path         the path to use
+     * @param lambda       the lambda to use
      * @return optical path intent
      */
     private Intent createIntent(OpticalConnectivityIntent parentIntent, Path path, OchSignal lambda) {
@@ -180,7 +187,7 @@
     /**
      * Convert given lambda as discrete resource of all path ports.
      *
-     * @param path the path
+     * @param path   the path
      * @param lambda the lambda
      * @return list of discrete resources
      */
@@ -197,7 +204,7 @@
     /**
      * Reserve all required resources for this intent.
      *
-     * @param intent the intent
+     * @param intent    the intent
      * @param resources list of resources to reserve
      */
     private void allocateResources(Intent intent, List<Resource> resources) {
@@ -252,7 +259,7 @@
 
     /**
      * Get the number of 12.5 GHz slots required for the path.
-     *
+     * <p>
      * For now this returns a constant value of 4 (i.e., fixed grid 50 GHz slot),
      * but in the future can depend on optical reach, line rate, transponder port capabilities, etc.
      *
@@ -284,7 +291,7 @@
      * Returns list of consecutive resources in given set of lambdas.
      *
      * @param lambdas list of lambdas
-     * @param count number of consecutive lambdas to return
+     * @param count   number of consecutive lambdas to return
      * @return list of consecutive lambdas
      */
     private List<OchSignal> findFirstLambda(Set<OchSignal> lambdas, int count) {
@@ -363,6 +370,24 @@
 
         ConnectPoint start = intent.getSrc();
         ConnectPoint end = intent.getDst();
+
+        // 0 hop case
+        if (start.deviceId().equals(end.deviceId())) {
+            log.debug("install optical intent for 0 hop i.e srcDeviceId=dstDeviceId");
+            DefaultLink defaultLink = DefaultLink.builder()
+                    .providerId(PROVIDER_ID)
+                    .src(start)
+                    .dst(end)
+                    .state(Link.State.ACTIVE)
+                    .type(Link.Type.DIRECT)
+                    .isExpected(true)
+                    .build();
+            List<Link> links = ImmutableList.<Link>builder().add(defaultLink).build();
+            Annotations annotations = DefaultAnnotations.builder().build();
+            DefaultPath defaultPath = new DefaultPath(PROVIDER_ID, links, null, annotations);
+            return ImmutableList.<Path>builder().add(defaultPath).build().stream();
+        }
+
         //head link's src port should be same as intent src port and tail link dst port
         //should be same as intent dst port in the path.
         Stream<Path> paths = topologyService.getKShortestPaths(topology,
@@ -376,9 +401,9 @@
                     .map(path -> {
                         // no-op map stage to add debug logging
                         log.debug("Candidate path: {}",
-                                  path.links().stream()
-                                          .map(lk -> lk.src() + "-" + lk.dst())
-                                          .collect(Collectors.toList()));
+                                path.links().stream()
+                                        .map(lk -> lk.src() + "-" + lk.dst())
+                                        .collect(Collectors.toList()));
                         return path;
                     });
         }
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java
index 0c777bf..9786cd4 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java
@@ -99,11 +99,11 @@
 
         return Collections.singletonList(
                 new FlowRuleIntent(appId,
-                                   intent.key(),
-                                   rules,
-                                   intent.resources(),
-                                   PathIntent.ProtectionType.PRIMARY,
-                                   intent.resourceGroup()
+                        intent.key(),
+                        rules,
+                        intent.resources(),
+                        PathIntent.ProtectionType.PRIMARY,
+                        intent.resourceGroup()
                 )
         );
     }
@@ -119,6 +119,32 @@
         selectorBuilder.matchInPort(intent.src().port());
 
         List<FlowRule> rules = new LinkedList<>();
+
+        /*
+         * especial case for 0 hop when srcDeviceId = dstDeviceId
+         * and path contain only one fake default path.
+         */
+        if (intent.src().deviceId().equals(intent.dst().deviceId()) &&
+                intent.path().links().size() == 1) {
+            log.debug("handling 0 hop case for intent {}", intent);
+            TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+            if (!isTransparent(intent.src().deviceId())) {
+                treatmentBuilder.add(Instructions.modL0Lambda(intent.lambda()));
+            }
+            treatmentBuilder.setOutput(intent.dst().port());
+
+            FlowRule rule = DefaultFlowRule.builder()
+                    .forDevice(intent.src().deviceId())
+                    .withSelector(selectorBuilder.build())
+                    .withTreatment(treatmentBuilder.build())
+                    .withPriority(intent.priority())
+                    .fromApp(appId)
+                    .makePermanent()
+                    .build();
+            rules.add(rule);
+            return rules;
+        }
+
         ConnectPoint current = intent.src();
 
         for (Link link : intent.path().links()) {
@@ -181,6 +207,32 @@
         selectorBuilder.matchInPort(intent.dst().port());
 
         List<FlowRule> rules = new LinkedList<>();
+
+        /*
+         * especial case for 0 hop when srcDeviceId = dstDeviceId
+         * and path contain only one fake default path.
+         */
+        if (intent.src().deviceId().equals(intent.dst().deviceId()) &&
+                intent.path().links().size() == 1) {
+            log.debug("handling 0 hop reverse path case for intent {}", intent);
+            TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+            if (!isTransparent(intent.src().deviceId())) {
+                treatmentBuilder.add(Instructions.modL0Lambda(intent.lambda()));
+            }
+            treatmentBuilder.setOutput(intent.src().port());
+
+            FlowRule rule = DefaultFlowRule.builder()
+                    .forDevice(intent.src().deviceId())
+                    .withSelector(selectorBuilder.build())
+                    .withTreatment(treatmentBuilder.build())
+                    .withPriority(intent.priority())
+                    .fromApp(appId)
+                    .makePermanent()
+                    .build();
+            rules.add(rule);
+            return rules;
+        }
+
         ConnectPoint current = intent.dst();
 
         for (Link link : Lists.reverse(intent.path().links())) {
@@ -254,7 +306,7 @@
     private boolean isTransparent(DeviceId deviceId) {
         return TRANSPARENT_DEVICES.contains(
                 Optional.ofNullable(deviceService.getDevice(deviceId))
-                .map(Device::type)
-                .orElse(Type.OTHER));
+                        .map(Device::type)
+                        .orElse(Type.OTHER));
     }
 }