[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/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);
+ }
+ }
+ }
+}