[ONOS-6137] Refactoring SecurityGroup features for stateful SONA

- Caches the security group information
- Supports seucrity group & rules update

Change-Id: Id7cdfb84e53e00aab49fbe736fb39e37492eeb61
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 00e91b0..a1d5ace 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -39,4 +39,6 @@
     public static final int PRIORITY_EXTERNAL_ROUTING_RULE = 25000;
     public static final int PRIORITY_SNAT_RULE = 26000;
     public static final int PRIORITY_SWITCHING_RULE = 30000;
+    public static final int PRIORITY_ACL_RULE = 30000;
+
 }
\ No newline at end of file
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
index c022c29..bfad9ed 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
@@ -21,6 +21,8 @@
 import org.openstack4j.model.network.Port;
 import org.openstack4j.model.network.Subnet;
 
+import java.util.Collection;
+
 import static com.google.common.base.MoreObjects.toStringHelper;
 
 /**
@@ -30,6 +32,7 @@
 
     private final Port port;
     private final Subnet subnet;
+    private final Collection<String> sgRuleIds;
 
     public enum Type {
         /**
@@ -75,7 +78,17 @@
         /**
          * Signifies that the OpenStack port is removed.
          */
-        OPENSTACK_PORT_REMOVED
+        OPENSTACK_PORT_REMOVED,
+
+        /**
+         * Signifies that the OpenStack security group rule is added to a specific port.
+         */
+        OPENSTACK_SECURITY_GROUP_ADDED_TO_PORT,
+
+        /**
+         * Signifies that the OpenStack security group rule is removed from a specific port.
+         */
+        OPENSTACK_SECURITY_GROUP_REMOVED_FROM_PORT
     }
 
     /**
@@ -87,6 +100,7 @@
         super(type, network);
         this.port = null;
         this.subnet = null;
+        this.sgRuleIds = null;
     }
 
     /**
@@ -101,6 +115,7 @@
         super(type, network);
         this.port = port;
         this.subnet = null;
+        this.sgRuleIds = null;
     }
 
     /**
@@ -115,6 +130,21 @@
         super(type, network);
         this.port = null;
         this.subnet = subnet;
+        this.sgRuleIds = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified port and security groups.
+     *
+     * @param type openstack network event type
+     * @param sgRuleIds openstack security group rules
+     * @param port openstack port
+     */
+    public OpenstackNetworkEvent(Type type, Collection<String> sgRuleIds, Port port) {
+        super(type, null);
+        this.port = port;
+        this.sgRuleIds = sgRuleIds;
+        this.subnet = null;
     }
 
     /**
@@ -135,6 +165,15 @@
         return subnet;
     }
 
+    /**
+     * Returns the security group rule IDs updated.
+     *
+     * @return collection of security group rule ID
+     */
+    public Collection<String> securityGroupRuleIds() {
+        return sgRuleIds;
+    }
+
     @Override
     public String toString() {
         if (port == null && subnet == null) {
@@ -146,6 +185,7 @@
                 .add("network", subject())
                 .add("port", port)
                 .add("subnet", subnet)
+                .add("security group rules", securityGroupRuleIds())
                 .toString();
     }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupAdminService.java
new file mode 100644
index 0000000..3b49c02
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupAdminService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+
+/**
+ * Service for administering the inventory of Openstack security group.
+ */
+public interface OpenstackSecurityGroupAdminService {
+    /**
+     * Creates a security group.
+     *
+     * @param sg security group
+     */
+    void createSecurityGroup(SecurityGroup sg);
+
+    /**
+     * Removes the security group.
+     *
+     * @param sgId security group ID
+     */
+    void removeSecurityGroup(String sgId);
+
+    /**
+     * Creates a security group rule.
+     *
+     * @param sgRule security group rule
+     */
+    void createSecurityGroupRule(SecurityGroupRule sgRule);
+
+    /**
+     * Removes the security group rule.
+     *
+     * @param sgRuleId security group rule ID
+     */
+    void removeSecurityGroupRule(String sgRuleId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupEvent.java
new file mode 100644
index 0000000..6f87a2a
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupEvent.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.AbstractEvent;
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+
+/**
+ * Describes OpenStack security group event.
+ */
+public class OpenstackSecurityGroupEvent
+        extends AbstractEvent<OpenstackSecurityGroupEvent.Type, SecurityGroup> {
+
+    private SecurityGroupRule sgRule;
+
+    public enum Type {
+        /**
+         * Signifies that a new OpenStack security group is created.
+         */
+        OPENSTACK_SECURITY_GROUP_CREATED,
+
+        /**
+         * Signifies that the OpenStack security group is removed.
+         */
+        OPENSTACK_SECURITY_GROUP_REMOVED,
+
+        /**
+         * Signifies that a new OpenStack security group rule is created.
+         */
+        OPENSTACK_SECURITY_GROUP_RULE_CREATED,
+
+        /**
+         * Signifies that the OpenStack security group rule is removed.
+         */
+        OPENSTACK_SECURITY_GROUP_RULE_REMOVED,
+    }
+
+    /**
+     * SecurityGroupEvent constructor.
+     *
+     * @param type SecurityGroupEvent type
+     * @param sg SecurityGroup object
+     */
+    public OpenstackSecurityGroupEvent(OpenstackSecurityGroupEvent.Type type, SecurityGroup sg) {
+        super(type, sg);
+    }
+
+    /**
+     * SecurityGroupEvent constructor.
+     *
+     * @param type SecurityGroupEvent type
+     * @param sgRule SecurityGroup object
+     */
+    public OpenstackSecurityGroupEvent(OpenstackSecurityGroupEvent.Type type, SecurityGroupRule sgRule) {
+        super(type, null);
+        this.sgRule = sgRule;
+    }
+
+    /**
+     * Returns security group rule.
+     *
+     * @return SecurityGroupRule
+     */
+    public SecurityGroupRule securityGroupRule() {
+        return this.sgRule;
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupListener.java
new file mode 100644
index 0000000..266844c
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for OpenStack Security Group events.
+ *
+ */
+public interface OpenstackSecurityGroupListener
+        extends EventListener<OpenstackSecurityGroupEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
new file mode 100644
index 0000000..bf3acb5
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+
+/**
+ * Service for interfacing OpenStack SecurityGroup events and SecurityGroup store.
+ *
+ */
+public interface OpenstackSecurityGroupService
+        extends ListenerService<OpenstackSecurityGroupEvent, OpenstackSecurityGroupListener> {
+
+    /**
+     * Returns the security group for the sgId.
+     *
+     * @param sgId security group Id
+     * @return security group
+     */
+    SecurityGroup securityGroup(String sgId);
+
+    /**
+     * Returns the security group rule for the sgRuleId given.
+     *
+     * @param sgRuleId security group rule Id
+     * @return security group rule
+     */
+    SecurityGroupRule securityGroupRule(String sgRuleId);
+
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupStore.java
new file mode 100644
index 0000000..f4c02a4
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupStore.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.store.Store;
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+
+/**
+ * Manages inventory of OpenStack security group states; not intended for direct use.
+ */
+public interface OpenstackSecurityGroupStore
+        extends Store<OpenstackSecurityGroupEvent, OpenstackSecurityGroupStoreDelegate> {
+
+    /**
+     * Creates a security group.
+     *
+     * @param sg security group
+     */
+    void createSecurityGroup(SecurityGroup sg);
+
+    /**
+     * Updates the security group with the security group ID with the security group object.
+     *
+     * @param sgId security group ID
+     * @param sg new SecurityGroup object
+     * @return old SecurityGroup object
+     */
+    SecurityGroup updateSecurityGroup(String sgId, SecurityGroup sg);
+
+    /**
+     * Removes the security group with the security group ID.
+     *
+     * @param sgId security group Id
+     * @return SecurityGroup object removed
+     */
+    SecurityGroup removeSecurityGroup(String sgId);
+
+    /**
+     * Creates a security group rule.
+     *
+     * @param sgRule security group rule
+     */
+    void createSecurityGroupRule(SecurityGroupRule sgRule);
+
+    /**
+     * Removes the security group rule with the security group rule ID.
+     *
+     * @param sgRuleId security group rule ID to remove
+     * @return SecurityGroupRule object removed
+     */
+    SecurityGroupRule removeSecurityGroupRule(String sgRuleId);
+
+    /**
+     * Returns the security group with the security group ID.
+     *
+     * @param sgId security group ID
+     * @return Security Group
+     */
+    SecurityGroup securityGroup(String sgId);
+
+    /**
+     * Returns the security group rule with the security group ID.
+     *
+     * @param sgRuleId security group rule ID
+     * @return Security Group Rule
+     */
+    SecurityGroupRule securityGroupRule(String sgRuleId);
+
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupStoreDelegate.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupStoreDelegate.java
new file mode 100644
index 0000000..e1489aa
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * OpenStack security group store delegate abstraction.
+ */
+public interface OpenstackSecurityGroupStoreDelegate extends StoreDelegate<OpenstackSecurityGroupEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
index ac29491..3170a88 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -16,12 +16,13 @@
 package org.onosproject.openstacknetworking.impl;
 
 import com.google.common.collect.ImmutableSet;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.Service;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -52,6 +53,7 @@
 import org.openstack4j.openstack.networking.domain.NeutronSubnet;
 import org.slf4j.Logger;
 
+import java.util.Collection;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
@@ -347,10 +349,29 @@
                 case UPDATE:
                     log.debug("OpenStack port updated {}", event.newValue());
                     eventExecutor.execute(() -> {
+                        Port oldPort = event.oldValue().value();
+                        Port newPort = event.newValue().value();
+
                         notifyDelegate(new OpenstackNetworkEvent(
                                 OPENSTACK_PORT_UPDATED,
-                                network(event.newValue().value().getNetworkId()),
-                                event.newValue().value()));
+                                network(event.newValue().value().getNetworkId()), newPort));
+
+                        if (!newPort.getSecurityGroups().equals(oldPort.getSecurityGroups())) {
+                            Collection<String> sgToAdd = CollectionUtils.subtract(newPort.getSecurityGroups(),
+                                    oldPort.getSecurityGroups());
+                            if (!sgToAdd.isEmpty()) {
+                                notifyDelegate(new OpenstackNetworkEvent(
+                                        OpenstackNetworkEvent.Type.OPENSTACK_SECURITY_GROUP_ADDED_TO_PORT,
+                                        sgToAdd, newPort));
+                            }
+                            Collection<String> sgToRemove = CollectionUtils.subtract(oldPort.getSecurityGroups(),
+                                    newPort.getSecurityGroups());
+                            if (!sgToRemove.isEmpty()) {
+                                notifyDelegate(new OpenstackNetworkEvent(
+                                        OpenstackNetworkEvent.Type.OPENSTACK_SECURITY_GROUP_REMOVED_FROM_PORT,
+                                        sgToRemove, newPort));
+                            }
+                        }
                     });
                     break;
                 case INSERT:
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedSecurityGroupStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedSecurityGroupStore.java
new file mode 100644
index 0000000..edbd422
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedSecurityGroupStore.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupStore;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+import org.openstack4j.openstack.networking.domain.NeutronSecurityGroup;
+import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
+import org.slf4j.Logger;
+
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of OpenStack security group using a {@code ConsistentMap}.
+ *
+ */
+@Service
+@Component(immediate = true)
+public class DistributedSecurityGroupStore
+        extends AbstractStore<OpenstackSecurityGroupEvent, OpenstackSecurityGroupStoreDelegate>
+        implements OpenstackSecurityGroupStore {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_DUPLICATE = " already exists";
+
+    private static final KryoNamespace SERIALIZER_SECURITY_GROUP = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(SecurityGroup.class)
+            .register(SecurityGroupRule.class)
+            .register(NeutronSecurityGroupRule.class)
+            .register(NeutronSecurityGroup.class)
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private final MapEventListener<String, SecurityGroup> securityGroupMapListener =
+            new OpenstackSecurityGroupMapListener();
+    private final MapEventListener<String, SecurityGroupRule> securityGroupRuleMapListener =
+            new OpenstackSecurityGroupRuleMapListener();
+
+    private ConsistentMap<String, SecurityGroup> osSecurityGroupStore;
+    private ConsistentMap<String, SecurityGroupRule> osSecurityGroupRuleStore;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+
+        osSecurityGroupStore = storageService.<String, SecurityGroup>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_SECURITY_GROUP))
+                .withName("openstack-securitygroupstore")
+                .withApplicationId(appId)
+                .build();
+        osSecurityGroupStore.addListener(securityGroupMapListener);
+
+        osSecurityGroupRuleStore = storageService.<String, SecurityGroupRule>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_SECURITY_GROUP))
+                .withName("openstack-securitygrouprulestore")
+                .withApplicationId(appId)
+                .build();
+        osSecurityGroupRuleStore.addListener(securityGroupRuleMapListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osSecurityGroupStore.removeListener(securityGroupMapListener);
+        osSecurityGroupRuleStore.removeListener(securityGroupRuleMapListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createSecurityGroup(SecurityGroup sg) {
+        osSecurityGroupStore.compute(sg.getId(), (id, existing) -> {
+            final String error = sg.getName() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return sg;
+        });
+    }
+
+    @Override
+    public SecurityGroup updateSecurityGroup(String sgId, SecurityGroup newSg) {
+        Versioned<SecurityGroup> sg = osSecurityGroupStore.replace(sgId, newSg);
+        return sg == null ? null : sg.value();
+    }
+
+    @Override
+    public SecurityGroup removeSecurityGroup(String sgId) {
+        Versioned<SecurityGroup> sg = osSecurityGroupStore.remove(sgId);
+        return sg == null ? null : sg.value();
+    }
+
+    @Override
+    public void createSecurityGroupRule(SecurityGroupRule sgRule) {
+        osSecurityGroupRuleStore.compute(sgRule.getId(), (id, existing) -> {
+            final String error = sgRule.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return sgRule;
+        });
+    }
+
+    @Override
+    public SecurityGroupRule removeSecurityGroupRule(String sgRuleId) {
+        Versioned<SecurityGroupRule> sgRule = osSecurityGroupRuleStore.remove(sgRuleId);
+        return sgRule == null ? null : sgRule.value();
+    }
+
+    @Override
+    public SecurityGroup securityGroup(String sgId) {
+        Versioned<SecurityGroup> osSg = osSecurityGroupStore.get(sgId);
+        return osSg == null ? null : osSg.value();
+    }
+
+    @Override
+    public SecurityGroupRule securityGroupRule(String sgRuleId) {
+        Versioned<SecurityGroupRule> osSgRule = osSecurityGroupRuleStore.get(sgRuleId);
+        return osSgRule == null ? null : osSgRule.value();
+    }
+
+    private class OpenstackSecurityGroupMapListener implements MapEventListener<String, SecurityGroup> {
+
+        @Override
+        public void event(MapEvent<String, SecurityGroup> event) {
+            switch (event.type()) {
+                case INSERT:
+                    log.debug("Openstack Security Group created {}", event.newValue());
+                    eventExecutor.execute(() ->
+                            notifyDelegate(new OpenstackSecurityGroupEvent(
+                                    OpenstackSecurityGroupEvent.Type.OPENSTACK_SECURITY_GROUP_CREATED,
+                                    securityGroup(event.newValue().value().getId()))));
+                    break;
+
+                case REMOVE:
+                    log.debug("Openstack Security Group removed {}", event.newValue());
+                    eventExecutor.execute(() ->
+                            notifyDelegate(new OpenstackSecurityGroupEvent(
+                                    OpenstackSecurityGroupEvent.Type.OPENSTACK_SECURITY_GROUP_REMOVED,
+                                    event.oldValue().value())));
+                    break;
+                default:
+            }
+        }
+    }
+
+    private class OpenstackSecurityGroupRuleMapListener implements MapEventListener<String, SecurityGroupRule> {
+
+        @Override
+        public void event(MapEvent<String, SecurityGroupRule> event) {
+            switch (event.type()) {
+                case INSERT:
+                    log.debug("Openstack Security Group Rule created {}", event.newValue());
+                    eventExecutor.execute(() ->
+                            notifyDelegate(new OpenstackSecurityGroupEvent(
+                                    OpenstackSecurityGroupEvent.Type.OPENSTACK_SECURITY_GROUP_RULE_CREATED,
+                                    securityGroupRule(event.newValue().value().getId()))));
+                    break;
+
+                case REMOVE:
+                    log.debug("Openstack Security Group Rule removed {}", event.oldValue());
+                    eventExecutor.execute(() ->
+                            notifyDelegate(new OpenstackSecurityGroupEvent(
+                                    OpenstackSecurityGroupEvent.Type.OPENSTACK_SECURITY_GROUP_RULE_REMOVED,
+                                    event.oldValue().value())));
+                    break;
+                default:
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
new file mode 100644
index 0000000..a3f135b
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
@@ -0,0 +1,434 @@
+/*
+* Copyright 2017-present Open Networking Laboratory
+*
+* 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.TpPort;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupListener;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ACL_RULE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Populates flow rules to handle OpenStack SecurityGroups.
+ */
+@Component(immediate = true)
+public class OpenstackSecurityGroupHandler {
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService openstackService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackSecurityGroupService securityGroupService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
+    private final InstancePortListener instancePortListener = new InternalInstancePortListener();
+    private final OpenstackNetworkListener portListener = new InternalOpenstackPortListener();
+    private final OpenstackSecurityGroupListener securityGroupListener = new InternalSecurityGroupListener();
+    private ApplicationId appId;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+
+    private static final String PROTO_ICMP = "ICMP";
+    private static final String PROTO_TCP = "TCP";
+    private static final String PROTO_UDP = "UDP";
+    private static final String ETHTYPE_IPV4 = "IPV4";
+    private static final String EGRESS = "EGRESS";
+    private static final String INGRESS = "INGRESS";
+    private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        instancePortService.addListener(instancePortListener);
+        securityGroupService.addListener(securityGroupListener);
+        openstackService.addListener(portListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        instancePortService.removeListener(instancePortListener);
+        securityGroupService.removeListener(securityGroupListener);
+        openstackService.removeListener(portListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    private void setSecurityGroupRules(InstancePort instPort, Port port, boolean install) {
+        port.getSecurityGroups().forEach(sgId -> {
+            log.debug("security group rule ID : " + sgId.toString());
+            SecurityGroup sg = securityGroupService.securityGroup(sgId);
+            if (sg == null) {
+                log.error("Security Group Not Found : {}", sgId);
+                return;
+            }
+            sg.getRules().forEach(sgRule -> updateSecurityGroupRule(instPort, port, sgRule, install));
+        });
+    }
+
+    private void updateSecurityGroupRule(InstancePort instPort, Port port, SecurityGroupRule sgRule, boolean install) {
+        if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
+            getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
+                .forEach(rInstPort -> {
+                    populateSecurityGroupRule(sgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
+                    populateSecurityGroupRule(sgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
+
+                    SecurityGroupRule rSgRule = new NeutronSecurityGroupRule.SecurityGroupRuleConcreteBuilder()
+                            .from(sgRule)
+                            .direction(sgRule.getDirection().toUpperCase().equals(EGRESS) ? INGRESS : EGRESS).build();
+                    populateSecurityGroupRule(rSgRule, instPort, rInstPort.ipAddress().toIpPrefix(), install);
+                    populateSecurityGroupRule(rSgRule, rInstPort, instPort.ipAddress().toIpPrefix(), install);
+                });
+        } else {
+            populateSecurityGroupRule(sgRule, instPort, sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
+                    IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
+        }
+    }
+
+    private void populateSecurityGroupRule(SecurityGroupRule sgRule, InstancePort instPort,
+                                           IpPrefix remoteIp, boolean install) {
+        ForwardingObjective.Builder foBuilder = buildFlowObjective(sgRule,
+                Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
+        if (foBuilder == null) {
+            return;
+        }
+
+        if (install) {
+            flowObjectiveService.forward(instPort.deviceId(), foBuilder.add());
+        } else {
+            flowObjectiveService.forward(instPort.deviceId(), foBuilder.remove());
+        }
+    }
+
+    /**
+     * Returns a set of host IP addresses engaged with supplied security group ID.
+     * It only searches a VM in the same tenant boundary.
+     *
+     * @param tenantId tenant id
+     * @param sgId security group id
+     * @return set of ip addresses
+     */
+    private Set<InstancePort> getRemoteInstPorts(String tenantId, String sgId) {
+        Set<InstancePort> remoteInstPorts;
+
+        remoteInstPorts = openstackService.ports().stream()
+                .filter(port -> port.getTenantId().equals(tenantId))
+                .filter(port -> port.getSecurityGroups().contains(sgId))
+                .map(port -> instancePortService.instancePort(port.getId()))
+                .filter(instPort -> instPort != null && instPort.ipAddress() != null)
+                .collect(Collectors.toSet());
+
+        return Collections.unmodifiableSet(remoteInstPorts);
+    }
+
+    private ForwardingObjective.Builder buildFlowObjective(SecurityGroupRule sgRule,
+                                                           Ip4Address vmIp,
+                                                           IpPrefix remoteIp) {
+        if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
+            // do nothing if the remote IP is my IP
+            return null;
+        }
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
+
+        return DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(DefaultTrafficTreatment.builder().build())
+                .withPriority(PRIORITY_ACL_RULE)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .fromApp(appId);
+    }
+
+    private void buildMatchs(TrafficSelector.Builder sBuilder, SecurityGroupRule sgRule,
+                             Ip4Address vmIp, IpPrefix remoteIp) {
+        buildMatchEthType(sBuilder, sgRule.getEtherType());
+        buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
+        buildMatchProto(sBuilder, sgRule.getProtocol());
+        buildMatchPort(sBuilder, sgRule.getProtocol(), sgRule.getDirection(),
+                sgRule.getPortRangeMax() == null ? 0 : sgRule.getPortRangeMax(),
+                sgRule.getPortRangeMin() == null ? 0 : sgRule.getPortRangeMin());
+        buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
+        if (sgRule.getRemoteGroupId() != null && sgRule.getRemoteGroupId().isEmpty()) {
+            buildMatchRemoteIp(sBuilder, remoteIp, sgRule.getDirection());
+        }
+    }
+
+    private void buildMatchDirection(TrafficSelector.Builder sBuilder,
+                                     String direction,
+                                     Ip4Address vmIp) {
+        if (direction.toUpperCase().equals(EGRESS)) {
+            sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
+        } else {
+            sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
+        }
+    }
+
+    private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
+        // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4);
+        if (etherType != null && !Objects.equals(etherType, "null") &&
+                !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
+            log.debug("EthType {} is not supported yet in Security Group", etherType);
+        }
+    }
+
+    private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix, String direction) {
+        if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
+            if (direction.toUpperCase().equals(EGRESS)) {
+                sBuilder.matchIPDst(remoteIpPrefix);
+            } else {
+                sBuilder.matchIPSrc(remoteIpPrefix);
+            }
+        }
+    }
+
+    private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
+        if (protocol != null) {
+            switch (protocol.toUpperCase()) {
+                case PROTO_ICMP:
+                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
+                    break;
+                case PROTO_TCP:
+                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
+                    break;
+                case PROTO_UDP:
+                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
+                    break;
+                default:
+            }
+        }
+    }
+
+    private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol, String direction,
+                                int portMin, int portMax) {
+        if (portMin > 0 && portMax > 0 && portMin == portMax) {
+            if (protocol.toUpperCase().equals(PROTO_TCP)) {
+                if (direction.toUpperCase().equals(EGRESS)) {
+                    sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
+                } else {
+                    sBuilder.matchTcpDst(TpPort.tpPort(portMax));
+                }
+            } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
+                if (direction.toUpperCase().equals(EGRESS)) {
+                    sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
+                } else {
+                    sBuilder.matchUdpDst(TpPort.tpPort(portMax));
+                }
+            }
+        }
+    }
+
+    private class InternalInstancePortListener implements InstancePortListener {
+
+        @Override
+        public boolean isRelevant(InstancePortEvent event) {
+            InstancePort instPort = event.subject();
+            return mastershipService.isLocalMaster(instPort.deviceId());
+        }
+
+        @Override
+        public void event(InstancePortEvent event) {
+            InstancePort instPort = event.subject();
+            switch (event.type()) {
+                case OPENSTACK_INSTANCE_PORT_UPDATED:
+                case OPENSTACK_INSTANCE_PORT_DETECTED:
+                    eventExecutor.execute(() -> {
+                        log.info("Instance port detected MAC:{} IP:{}",
+                                instPort.macAddress(),
+                                instPort.ipAddress());
+                        instPortDetected(event.subject(), openstackService.port(event.subject().portId()));
+                    });
+                    break;
+                case OPENSTACK_INSTANCE_PORT_VANISHED:
+                    eventExecutor.execute(() -> {
+                        log.info("Instance port vanished MAC:{} IP:{}",
+                                instPort.macAddress(),
+                                instPort.ipAddress());
+                        instPortRemoved(event.subject(), openstackService.port(event.subject().portId()));
+                    });
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void instPortDetected(InstancePort instPort, Port port) {
+            setSecurityGroupRules(instPort, port, true);
+        }
+
+        private void instPortRemoved(InstancePort instPort, Port port) {
+            setSecurityGroupRules(instPort, port, false);
+        }
+    }
+
+    private class InternalOpenstackPortListener implements OpenstackNetworkListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNetworkEvent event) {
+            Port osPort = event.port();
+            if (osPort == null) {
+                return false;
+            }
+            return !Strings.isNullOrEmpty(osPort.getId());
+        }
+
+        @Override
+        public void event(OpenstackNetworkEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_SECURITY_GROUP_ADDED_TO_PORT:
+                    securityGroupAddedToPort(event.securityGroupRuleIds(), event.port());
+                    break;
+                case OPENSTACK_SECURITY_GROUP_REMOVED_FROM_PORT:
+                    securityGroupRemovedFromPort(event.securityGroupRuleIds(), event.port());
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void securityGroupAddedToPort(Collection<String> sgToAdd, Port osPort) {
+            sgToAdd.forEach(sg -> {
+                InstancePort instPort = instancePortService.instancePort(osPort.getId());
+                if (instPort != null) {
+                    securityGroupService.securityGroup(sg).getRules().stream()
+                            .forEach(sgRule -> updateSecurityGroupRule(instancePortService.instancePort(
+                                    osPort.getId()), osPort, sgRule, true));
+                }
+            });
+        }
+
+        private void securityGroupRemovedFromPort(Collection<String> sgToRemove, Port osPort) {
+            sgToRemove.forEach(sg -> {
+                InstancePort instPort = instancePortService.instancePort(osPort.getId());
+                if (instPort != null) {
+                    securityGroupService.securityGroup(sg).getRules().stream()
+                            .forEach(sgRule -> updateSecurityGroupRule(instancePortService.instancePort(
+                                    osPort.getId()), osPort, sgRule, false));
+                }
+            });
+        }
+    }
+
+    private class InternalSecurityGroupListener implements OpenstackSecurityGroupListener {
+
+        @Override
+        public void event(OpenstackSecurityGroupEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_SECURITY_GROUP_CREATED:
+                case OPENSTACK_SECURITY_GROUP_REMOVED:
+                    break;
+                case OPENSTACK_SECURITY_GROUP_RULE_CREATED:
+                    SecurityGroupRule securityGroupRuleToAdd = event.securityGroupRule();
+                    eventExecutor.execute(() -> {
+                        log.info("Security group rule detected: ID {}",
+                                securityGroupRuleToAdd.getId());
+                        securityGroupRuleAdded(securityGroupRuleToAdd);
+                    });
+                    break;
+
+                case OPENSTACK_SECURITY_GROUP_RULE_REMOVED:
+                    SecurityGroupRule securityGroupRuleToRemove = event.securityGroupRule();
+                    eventExecutor.execute(() -> {
+                        log.info("security gorup rule removed: ID {}",
+                                securityGroupRuleToRemove.getId());
+                        securityGroupRuleRemoved(securityGroupRuleToRemove);
+                    });
+                    break;
+                default:
+            }
+        }
+
+        private void securityGroupRuleAdded(SecurityGroupRule sgRule) {
+            log.debug("securityGroupRuleAdded : {}" + sgRule);
+
+            openstackService.ports().stream()
+                    .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
+                    .forEach(port -> updateSecurityGroupRule(instancePortService.instancePort(port.getId()),
+                            port, sgRule, true));
+        }
+
+        private void securityGroupRuleRemoved(SecurityGroupRule sgRule) {
+            log.debug("securityGroupRuleRemoved : {}" + sgRule);
+
+            openstackService.ports().stream()
+                    .filter(port -> port.getSecurityGroups().contains(sgRule.getSecurityGroupId()))
+                    .forEach(port -> updateSecurityGroupRule(instancePortService.instancePort(port.getId()),
+                            port, sgRule, false));
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
new file mode 100644
index 0000000..678d180
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+
+import com.google.common.base.Strings;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupEvent;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupListener;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupStore;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupStoreDelegate;
+import org.openstack4j.model.network.SecurityGroup;
+import org.openstack4j.model.network.SecurityGroupRule;
+import org.openstack4j.openstack.networking.domain.NeutronSecurityGroup;
+import org.slf4j.Logger;
+
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfaceing Openstack security
+ * groups.
+ *
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackSecurityGroupManager
+        extends ListenerRegistry<OpenstackSecurityGroupEvent, OpenstackSecurityGroupListener>
+        implements OpenstackSecurityGroupAdminService, OpenstackSecurityGroupService {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String MSG_SG = "OpenStack security group %s %s";
+    private static final String MSG_SG_RULE = "OpenStack security group %s %s";
+
+
+    private static final String MSG_CREATED = "created";
+    private static final String MSG_REMOVED = "removed";
+
+    private static final String ERR_NULL_SG = "OpenStack security group cannot be null";
+    private static final String ERR_NULL_SG_ID = "OpenStack security group ID cannot be null";
+    private static final String ERR_NULL_SG_RULE = "OpenStack security group rule cannot be null";
+    private static final String ERR_NULL_SG_RULE_ID = "OpenStack security group rule ID cannot be null";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackSecurityGroupStore osSecurityGroupStore;
+
+    private final OpenstackSecurityGroupStoreDelegate delegate = new InternalSecurityGroupStoreDelegate();
+
+    @Activate
+    protected void activate() {
+        coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+        osSecurityGroupStore.setDelegate(delegate);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osSecurityGroupStore.unsetDelegate(delegate);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createSecurityGroup(SecurityGroup sg) {
+        checkNotNull(sg, ERR_NULL_SG);
+        checkArgument(!Strings.isNullOrEmpty(sg.getId()), ERR_NULL_SG_ID);
+
+        osSecurityGroupStore.createSecurityGroup(sg);
+        log.info(String.format(MSG_SG, sg.getId(), MSG_CREATED));
+    }
+
+    @Override
+    public void removeSecurityGroup(String sgId) {
+        checkNotNull(sgId, ERR_NULL_SG_ID);
+
+        osSecurityGroupStore.removeSecurityGroup(sgId);
+        log.info(String.format(MSG_SG, sgId, MSG_REMOVED));
+    }
+
+    @Override
+    public void createSecurityGroupRule(SecurityGroupRule sgRule) {
+        checkNotNull(sgRule, ERR_NULL_SG_RULE);
+        checkArgument(!Strings.isNullOrEmpty(sgRule.getId()), ERR_NULL_SG_RULE_ID);
+
+        synchronized (osSecurityGroupStore) {
+            SecurityGroup sg = securityGroup(sgRule.getSecurityGroupId());
+            List sgRules = sg.getRules();
+            sgRules.add(sgRule);
+            SecurityGroup newSg = new NeutronSecurityGroup.SecurityGroupConcreteBuilder().from(sg).build();
+            SecurityGroup oldSg = osSecurityGroupStore.updateSecurityGroup(sgRule.getSecurityGroupId(), newSg);
+            if (oldSg == null) {
+                log.warn("Failed to add the security group rule {} to security group", sgRule.getId());
+            }
+
+            osSecurityGroupStore.createSecurityGroupRule(sgRule);
+            log.info(String.format(MSG_SG_RULE, sgRule.getId(), MSG_CREATED));
+        }
+    }
+
+    @Override
+    public void removeSecurityGroupRule(String sgRuleId) {
+        checkNotNull(sgRuleId, ERR_NULL_SG_RULE_ID);
+
+        osSecurityGroupStore.removeSecurityGroupRule(sgRuleId);
+        log.info(String.format(MSG_SG_RULE, sgRuleId, MSG_REMOVED));
+    }
+
+    @Override
+    public SecurityGroup securityGroup(String sgId) {
+        checkArgument(!Strings.isNullOrEmpty(sgId), ERR_NULL_SG_ID);
+        return osSecurityGroupStore.securityGroup(sgId);
+    }
+
+    @Override
+    public SecurityGroupRule securityGroupRule(String sgRuleId) {
+        checkArgument(!Strings.isNullOrEmpty(sgRuleId), ERR_NULL_SG_RULE_ID);
+        return osSecurityGroupStore.securityGroupRule(sgRuleId);
+    }
+
+    private class InternalSecurityGroupStoreDelegate implements OpenstackSecurityGroupStoreDelegate {
+
+        @Override
+        public void notify(OpenstackSecurityGroupEvent event) {
+            if (event != null) {
+                log.trace("send openstack security group event {}", event);
+                process(event);
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSecurityGroupRuleWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSecurityGroupRuleWebResource.java
new file mode 100644
index 0000000..27ffa6f
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSecurityGroupRuleWebResource.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
+import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronSecurityGroupRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.InputStream;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+
+/**
+ * Handles Security Group Rule Rest API call from Neutron ML2 plugin.
+ */
+@Path("security-group-rules")
+public class OpenstackSecurityGroupRuleWebResource extends AbstractWebResource {
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String MESSAGE = "Received security group rules %s request";
+    private static final String SECURITY_GROUP_RULES = "security-group-rules";
+
+    private final OpenstackSecurityGroupAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackSecurityGroupAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a security group from the JSON input stream.
+     *
+     * @param input security group JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated port already exists
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createSecurityGroupRules(InputStream input) {
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronSecurityGroupRule sgRule = readSecurityGroupRule(input);
+        adminService.createSecurityGroupRule(sgRule);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(SECURITY_GROUP_RULES)
+                .path(sgRule.getId());
+
+        return created(locationBuilder.build()).build();
+    }
+
+    /**
+     * Removes the security group rule.
+     *
+     * @param id security group rule ID
+     * @return 204 NO_CONTENT
+     */
+    @DELETE
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response removeSecurityGroupRule(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "REMOVE " + id));
+
+        adminService.removeSecurityGroupRule(id);
+        return noContent().build();
+    }
+
+    private NeutronSecurityGroupRule readSecurityGroupRule(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronSecurityGroupRule.class)
+                    .readerFor(NeutronSecurityGroupRule.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSecurityGroupWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSecurityGroupWebResource.java
new file mode 100644
index 0000000..3a47e5d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSecurityGroupWebResource.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
+import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronSecurityGroup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.InputStream;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+
+/**
+ * Handles Security Group Rest API call from Neutron ML2 plugin.
+ */
+@Path("security-groups")
+public class OpenstackSecurityGroupWebResource extends AbstractWebResource {
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String MESSAGE = "Received security groups %s request";
+    private static final String SECURITY_GROUPS = "security-groups";
+
+    private final OpenstackSecurityGroupAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackSecurityGroupAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a security group from the JSON input stream.
+     *
+     * @param input security group JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated port already exists
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createSecurityGroups(InputStream input) {
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronSecurityGroup sg = readSecurityGroup(input);
+        adminService.createSecurityGroup(sg);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(SECURITY_GROUPS)
+                .path(sg.getId());
+
+        return created(locationBuilder.build()).build();
+    }
+
+    /**
+     * Updates a security group from the JSON input stream.
+     *
+     * @param input security group JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated port already exists
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateSecurityGroups(InputStream input) {
+        log.trace(String.format(MESSAGE, "UPDATE"));
+
+        final NeutronSecurityGroup sg = readSecurityGroup(input);
+        // Do nothing ..
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(SECURITY_GROUPS)
+                .path(sg.getId());
+
+        return created(locationBuilder.build()).build();
+    }
+
+    /**
+     * Removes the security group.
+     *
+     * @param id security group ID
+     * @return 204 NO_CONTENT
+     */
+    @DELETE
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response removeSecurityGroup(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "REMOVE " + id));
+
+        adminService.removeSecurityGroup(id);
+        return noContent().build();
+    }
+
+    private NeutronSecurityGroup readSecurityGroup(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronSecurityGroup.class)
+                    .readerFor(NeutronSecurityGroup.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/webapp/WEB-INF/web.xml b/apps/openstacknetworking/src/main/webapp/WEB-INF/web.xml
index 93e95f0..3f4ea91 100644
--- a/apps/openstacknetworking/src/main/webapp/WEB-INF/web.xml
+++ b/apps/openstacknetworking/src/main/webapp/WEB-INF/web.xml
@@ -30,7 +30,9 @@
                 org.onosproject.openstacknetworking.web.OpenstackNetworkWebResource,
                 org.onosproject.openstacknetworking.web.OpenstackSubnetWebResource,
                 org.onosproject.openstacknetworking.web.OpensatckRouterWebResource,
-                org.onosproject.openstacknetworking.web.OpenstackFloatingIpWebResource
+                org.onosproject.openstacknetworking.web.OpenstackFloatingIpWebResource,
+                org.onosproject.openstacknetworking.web.OpenstackSecurityGroupWebResource,
+                org.onosproject.openstacknetworking.web.OpenstackSecurityGroupRuleWebResource
             </param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenstackPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenstackPipeline.java
index b74b462..dbb5df5 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenstackPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenstackPipeline.java
@@ -61,9 +61,10 @@
     protected FlowRuleService flowRuleService;
 
     private static final int SRC_VNI_TABLE = 0;
-    private static final int JUMP_TABLE = 1;
-    private static final int ROUTING_TABLE = 2;
-    private static final int FORWARDING_TABLE = 3;
+    private static final int ACL_TABLE = 1;
+    private static final int JUMP_TABLE = 2;
+    private static final int ROUTING_TABLE = 3;
+    private static final int FORWARDING_TABLE = 4;
     private static final int DUMMY_TABLE = 10;
     private static final int LAST_TABLE = FORWARDING_TABLE;
 
@@ -125,8 +126,9 @@
     }
 
     private void initializePipeline() {
-        //TODO: For now, we do not support security group feature temporarily.
-        connectTables(SRC_VNI_TABLE, JUMP_TABLE); // Table 0 -> Table 1
+        connectTables(SRC_VNI_TABLE, ACL_TABLE);
+        connectTables(ACL_TABLE, JUMP_TABLE);
+        setUpTableMissEntry(ACL_TABLE);
         setupJumpTable();
     }
 
@@ -149,6 +151,25 @@
         applyRules(true, flowRule);
     }
 
+    private void setUpTableMissEntry(int table) {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+
+        treatment.drop();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .withPriority(DROP_PRIORITY)
+                .fromApp(appId)
+                .makePermanent()
+                .forTable(table)
+                .build();
+
+        applyRules(true, flowRule);
+    }
+
     private void setupJumpTable() {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
         TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
@@ -274,6 +295,13 @@
             return ROUTING_TABLE;
         } else if (output.isPresent() || (ipDst != null && group.isPresent())) {
             return FORWARDING_TABLE;
+        } else if ((ipSrc != null && ipSrc.ip().prefixLength() == 32 &&
+                ipDst != null && ipDst.ip().prefixLength() == 32) ||
+                (ipSrc != null && ipSrc.ip().prefixLength() == 32 && ipDst == null) ||
+                (ipDst != null && ipDst.ip().prefixLength() == 32 && ipSrc == null) ||
+                (ipDst != null && ipDst.ip().prefixLength() == 32 && ipSrc != null) ||
+                (ipSrc != null && ipSrc.ip().prefixLength() == 32 && ipDst != null)) {
+            return ACL_TABLE;
         }
 
         return DUMMY_TABLE;