[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;