sangho
Committed by Gerrit Code Review

[ONOS-3953] Implements Security Group of Openstack

Change-Id: I30766097a2894a26e46a7a399176d99e95af6abf
......@@ -15,6 +15,8 @@
*/
package org.onosproject.openstackinterface;
import org.onlab.packet.IpPrefix;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -24,25 +26,34 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class OpenstackSecurityGroupRule {
private final String direction;
private final Direction direction;
private final String ethertype;
private final String id;
private final String portRangeMax;
private final String portRangeMin;
private final int portRangeMax;
private final int portRangeMin;
private final String protocol;
private final String remoteGroupId;
private final String remoteIpPrefix;
private final IpPrefix remoteIpPrefix;
private final String secuityGroupId;
private final String tenantId;
private OpenstackSecurityGroupRule(String direction,
/**
* Direction of the Security Group.
*
*/
public enum Direction {
INGRESS,
EGRESS
}
private OpenstackSecurityGroupRule(Direction direction,
String ethertype,
String id,
String portRangeMax,
String portRangeMin,
int portRangeMax,
int portRangeMin,
String protocol,
String remoteGroupId,
String remoteIpPrefix,
IpPrefix remoteIpPrefix,
String securityGroupId,
String tenantId) {
this.direction = direction;
......@@ -57,6 +68,105 @@ public final class OpenstackSecurityGroupRule {
this.tenantId = tenantId;
}
/**
* Returns the builder object for the OpenstackSecurityGroupRule.
*
* @return OpenstackSecurityGroupRule builder object
*/
public static OpenstackSecurityGroupRule.Builder builder() {
return new Builder();
}
/**
* Returns the direction.
*
* @return direction
*/
public Direction direction() {
return direction;
}
/**
* Returns the Ethernet type.
*
* @return Ethernet type
*/
public String ethertype() {
return ethertype;
}
/**
* Returns the Security Group ID.
*
* @return Security Group ID
*/
public String id() {
return id;
}
/**
* Returns the max of the port range.
*
* @return max of the port range
*/
public int portRangeMax() {
return portRangeMax;
}
/**
* Returns the min of the port range.
*
* @return min of the port range
*/
public int portRangeMin() {
return portRangeMin;
}
/**
* Returns the IP protocol.
*
* @return IP protocol
*/
public String protocol() {
return protocol;
}
/**
* Returns the remote group ID.
*
* @return remote group ID
*/
public String remoteGroupId() {
return remoteGroupId;
}
/**
* Returns the remote IP address.
*
* @return remote IP address
*/
public IpPrefix remoteIpPrefix() {
return this.remoteIpPrefix;
}
/**
* Returns the Security Group ID.
*
* @return security group ID
*/
public String secuityGroupId() {
return secuityGroupId;
}
/**
* Returns the tenant ID.
*
* @return tenant ID
*/
public String tenantId() {
return tenantId;
}
@Override
public String toString() {
return new StringBuilder(" [")
......@@ -84,8 +194,8 @@ public final class OpenstackSecurityGroupRule {
return this.direction.equals(that.direction) &&
this.ethertype.equals(that.direction) &&
this.id.equals(that.id) &&
this.portRangeMax.equals(that.portRangeMax) &&
this.portRangeMin.equals(that.portRangeMin) &&
this.portRangeMax == that.portRangeMax &&
this.portRangeMin == that.portRangeMin &&
this.protocol.equals(that.protocol) &&
this.remoteGroupId.equals(that.remoteGroupId) &&
this.secuityGroupId.equals(that.secuityGroupId) &&
......@@ -235,8 +345,16 @@ public final class OpenstackSecurityGroupRule {
* @return OpenstackSecurityGroupRule object
*/
public OpenstackSecurityGroupRule build() {
return new OpenstackSecurityGroupRule(direction, etherType, id, portRangeMax,
portRangeMin, protocol, remoteGroupId, remoteIpPrefix, secuityGroupId, tenantId);
int portRangeMinInt = (portRangeMin == null || portRangeMin.equals("null")) ?
-1 : Integer.parseInt(portRangeMax);
int portRangeMaxInt = (portRangeMax == null || portRangeMax.equals("null")) ?
-1 : Integer.parseInt(portRangeMax);
IpPrefix ipPrefix = (remoteIpPrefix == null || remoteIpPrefix.equals("null")) ?
null : IpPrefix.valueOf(remoteIpPrefix);
return new OpenstackSecurityGroupRule(Direction.valueOf(direction.toUpperCase()), etherType, id,
portRangeMaxInt, portRangeMinInt, protocol, remoteGroupId, ipPrefix, secuityGroupId, tenantId);
}
}
}
......
......@@ -48,7 +48,7 @@ public class OpenstackSecurityGroupCodec extends JsonCodec<OpenstackSecurityGrou
private static final String REMOTE_GROUP_ID = "remote_group_id";
private static final String REMOTE_IP_PREFIX = "remote_ip_prefix";
private static final String SECURITY_GROUP_ID = "security_group_id";
private static final String TENAN_ID = "tenant_id";
private static final String TENANT_ID = "tenant_id";
@Override
public OpenstackSecurityGroup decode(ObjectNode json, CodecContext context) {
......@@ -75,12 +75,12 @@ public class OpenstackSecurityGroupCodec extends JsonCodec<OpenstackSecurityGrou
.remoteGroupId(ruleInfo.path(REMOTE_GROUP_ID).asText())
.remoteIpPrefix(ruleInfo.path(REMOTE_IP_PREFIX).asText())
.securityGroupId(ruleInfo.path(SECURITY_GROUP_ID).asText())
.tenantId(ruleInfo.path(TENAN_ID).asText())
.tenantId(ruleInfo.path(TENANT_ID).asText())
.build();
rules.add(openstackSecurityGroupRule);
}
String tenantId = securityGroupNode.path(TENAN_ID).asText();
String tenantId = securityGroupNode.path(TENANT_ID).asText();
OpenstackSecurityGroup openstackSecurityGroup = OpenstackSecurityGroup.builder()
.description(description)
......
......@@ -18,6 +18,10 @@ package org.onosproject.openstacknetworking;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.net.DeviceId;
import java.util.Collection;
import java.util.Collections;
import static com.google.common.base.Preconditions.checkNotNull;
/**
......@@ -29,70 +33,174 @@ public class OpenstackPortInfo {
private final DeviceId deviceId;
private final long vni;
private final Ip4Address gatewayIP;
private final Collection<String> securityGroups;
public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, long vni, Ip4Address gatewayIP) {
/**
* Returns OpenstackPortInfo reference.
*
* @param hostIp host IP address
* @param hostMac host MAC address
* @param deviceId device ID
* @param vni tunnel ID
* @param gatewayIP gateway IP address
* @param securityGroups security group list
*/
public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, long vni,
Ip4Address gatewayIP, Collection<String> securityGroups) {
this.hostIp = hostIp;
this.hostMac = hostMac;
this.deviceId = deviceId;
this.vni = vni;
this.gatewayIP = gatewayIP;
this.securityGroups = securityGroups;
}
/**
* Returns IP address of the port.
*
* @return IP address
*/
public Ip4Address ip() {
return hostIp;
}
/**
* Returns MAC address of the port.
*
* @return MAC address
*/
public MacAddress mac() {
return hostMac;
}
/**
* Returns device ID.
*
* @return device ID
*/
public DeviceId deviceId() {
return deviceId;
}
/**
* Returns tunnel ID.
*
* @return tunnel ID
*/
public long vni() {
return vni;
}
/**
* Returns gateway IP address.
*
* @return gateway IP address
*/
public Ip4Address gatewayIP() {
return gatewayIP;
}
/**
* Returns Security Group ID list.
*
* @return list of Security Group ID
*/
public Collection<String> securityGroups() {
return Collections.unmodifiableCollection(securityGroups);
}
/**
* Returns the builder of the OpenstackPortInfo.
*
* @return OpenstackPortInfo builder reference
*/
public static OpenstackPortInfo.Builder builder() {
return new Builder();
}
/**
* Represents the OpenstackPortInfo Builder.
*
*/
public static final class Builder {
private Ip4Address hostIp;
private MacAddress hostMac;
private DeviceId deviceId;
private long vni;
private Ip4Address gatewayIP;
private Collection<String> securityGroups;
/**
* Sets the IP address of the port.
*
* @param gatewayIP
* @return Builder reference
*/
public Builder setGatewayIP(Ip4Address gatewayIP) {
this.gatewayIP = checkNotNull(gatewayIP, "gatewayIP cannot be null");
return this;
}
/**
* Sets the host IP address of the port.
*
* @param hostIp host IP address
* @return Builder reference
*/
public Builder setHostIp(Ip4Address hostIp) {
this.hostIp = checkNotNull(hostIp, "hostIp cannot be null");
return this;
}
/**
* Sets the host MAC address of the port.
*
* @param hostMac host MAC address
* @return Builder reference
*/
public Builder setHostMac(MacAddress hostMac) {
this.hostMac = checkNotNull(hostMac, "hostMac cannot be bull");
return this;
}
/**
* Sets the device ID.
*
* @param deviceId device ID
* @return Builder reference
*/
public Builder setDeviceId(DeviceId deviceId) {
this.deviceId = checkNotNull(deviceId, "deviceId cannot be null");
return this;
}
/**
* Sets the tunnel ID.
*
* @param vni tunnel ID
* @return Builder reference
*/
public Builder setVni(long vni) {
this.vni = checkNotNull(vni, "vni cannot be null");
return this;
}
/**
* Sets the security group ID list.
*
* @param securityGroups security group ID list
* @return Builder reference
*/
public Builder setSecurityGroups(Collection<String> securityGroups) {
this.securityGroups = securityGroups;
return this;
}
/**
* Builds the OpenstackPortInfo reference.
*
* @return OpenstackPortInfo reference
*/
public OpenstackPortInfo build() {
return new OpenstackPortInfo(this);
}
......@@ -104,5 +212,6 @@ public class OpenstackPortInfo {
deviceId = builder.deviceId;
vni = builder.vni;
gatewayIP = builder.gatewayIP;
securityGroups = builder.securityGroups;
}
}
......
......@@ -104,7 +104,9 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
public static final String DEVICE_OWNER_GATEWAY = "network:router_gateway";
private ApplicationId appId;
private OpenstackArpHandler arpHandler = new OpenstackArpHandler(openstackService, packetService, hostService);
private OpenstackArpHandler arpHandler;
private OpenstackSecurityGroupRulePopulator sgRulePopulator;
private ExecutorService deviceEventExcutorService =
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event"));
......@@ -114,6 +116,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
private InternalHostListener internalHostListener = new InternalHostListener();
private Map<String, OpenstackPortInfo> openstackPortInfoMap = Maps.newHashMap();
private Map<String, OpenstackSecurityGroup> securityGroupMap = Maps.newConcurrentMap();
@Activate
protected void activate() {
......@@ -124,6 +127,9 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
deviceService.addListener(internalDeviceListener);
hostService.addListener(internalHostListener);
arpHandler = new OpenstackArpHandler(openstackService, packetService, hostService);
sgRulePopulator = new OpenstackSecurityGroupRulePopulator(appId, openstackService, flowObjectiveService);
initializeFlowRules();
log.info("Started");
......@@ -148,13 +154,6 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
registerDhcpInfo(openstackPort);
}
}
if (!openstackPort.securityGroups().isEmpty()) {
openstackPort.securityGroups().forEach(sgId -> {
OpenstackSecurityGroup sg = openstackService.getSecurityGroup(sgId);
log.debug("SecurityGroup : {}", sg.toString());
});
}
}
@Override
......@@ -185,6 +184,22 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
@Override
public void updatePort(OpenstackPort openstackPort) {
if (openstackPort.status().equals(OpenstackPort.PortStatus.ACTIVE)) {
String portName = PORTNAME_PREFIX_VM + openstackPort.id().substring(0, 11);
OpenstackPortInfo osPortInfo = openstackPortInfoMap.get(portName);
if (osPortInfo != null) {
// Remove all security group rules based on the ones stored in security group map.
osPortInfo.securityGroups().stream().forEach(
sgId -> sgRulePopulator.removeSecurityGroupRules(osPortInfo.deviceId(), sgId,
osPortInfo.ip(), openstackPortInfoMap, securityGroupMap));
// Add all security group rules based on the updated security group.
openstackPort.securityGroups().stream().forEach(
sgId -> sgRulePopulator.populateSecurityGroupRules(osPortInfo.deviceId(), sgId,
osPortInfo.ip(), openstackPortInfoMap));
updatePortMap(osPortInfo.deviceId(), portName, openstackService.networks(),
openstackService.subnets(), openstackPort);
}
}
}
@Override
......@@ -205,29 +220,40 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
}
private void processPortUpdated(Device device, Port port) {
if (!port.annotations().value(PORTNAME).equals(PORTNAME_PREFIX_TUNNEL)) {
if (port.isEnabled() || port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_ROUTER)) {
String portName = port.annotations().value(PORTNAME);
synchronized (openstackPortInfoMap) {
if (portName.startsWith(PORTNAME_PREFIX_VM)) {
if (port.isEnabled()) {
OpenstackSwitchingRulePopulator rulePopulator =
new OpenstackSwitchingRulePopulator(appId, flowObjectiveService,
deviceService, openstackService, driverService);
rulePopulator.populateSwitchingRules(device, port);
updatePortMap(device.id(), port, openstackService.networks(), openstackService.subnets(),
rulePopulator.openstackPort(port));
OpenstackPort openstackPort = rulePopulator.openstackPort(port);
Ip4Address vmIp = (Ip4Address) openstackPort.fixedIps().values().stream()
.findAny().orElseGet(null);
openstackPort.securityGroups().stream().forEach(
sgId -> sgRulePopulator.populateSecurityGroupRules(device.id(), sgId, vmIp,
openstackPortInfoMap));
updatePortMap(device.id(), port.annotations().value(PORTNAME),
openstackService.networks(), openstackService.subnets(), openstackPort);
//In case portupdate event is driven by vm shutoff from openstack
} else if (!port.isEnabled() && openstackPortInfoMap.containsKey(port.annotations().value(PORTNAME))) {
} else if (!port.isEnabled() && openstackPortInfoMap.containsKey(portName)) {
log.debug("Flowrules according to the port {} were removed", port.number().toString());
OpenstackSwitchingRulePopulator rulePopulator =
new OpenstackSwitchingRulePopulator(appId, flowObjectiveService,
deviceService, openstackService, driverService);
rulePopulator.removeSwitchingRules(port, openstackPortInfoMap);
openstackPortInfoMap.get(portName).securityGroups().stream().forEach(
sgId -> sgRulePopulator.removeSecurityGroupRules(device.id(), sgId,
openstackPortInfoMap.get(portName).ip(), openstackPortInfoMap, securityGroupMap));
dhcpService.removeStaticMapping(openstackPortInfoMap.get(port.annotations().value(PORTNAME)).mac());
openstackPortInfoMap.remove(port.annotations().value(PORTNAME));
}
}
}
}
private void processPortRemoved(Device device, Port port) {
log.debug("port {} is removed", port.toString());
......@@ -251,7 +277,13 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
OpenstackPort osPort = rulePopulator.openstackPort(vmPort);
if (osPort != null && !osPort.deviceOwner().equals(DEVICE_OWNER_GATEWAY)) {
rulePopulator.populateSwitchingRules(device, vmPort);
updatePortMap(device.id(), vmPort, networks, subnets, osPort);
Ip4Address vmIp = (Ip4Address) osPort.fixedIps().values().stream()
.findAny().orElseGet(null);
osPort.securityGroups().stream().forEach(
sgId -> sgRulePopulator.populateSecurityGroupRules(device.id(),
sgId, vmIp, openstackPortInfoMap));
updatePortMap(device.id(), vmPort.annotations().value(PORTNAME), networks,
subnets, osPort);
registerDhcpInfo(osPort);
} else {
log.warn("No openstackPort information for port {}", vmPort);
......@@ -262,7 +294,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
);
}
private void updatePortMap(DeviceId deviceId, Port port, Collection<OpenstackNetwork> networks,
private void updatePortMap(DeviceId deviceId, String portName, Collection<OpenstackNetwork> networks,
Collection<OpenstackSubnet> subnets, OpenstackPort openstackPort) {
long vni = Long.parseLong(networks.stream()
.filter(n -> n.id().equals(openstackPort.networkId()))
......@@ -279,10 +311,17 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
.setHostIp((Ip4Address) openstackPort.fixedIps().values().stream().findFirst().orElse(null))
.setHostMac(openstackPort.macAddress())
.setVni(vni)
.setGatewayIP(gatewayIPAddress);
.setGatewayIP(gatewayIPAddress)
.setSecurityGroups(openstackPort.securityGroups());
openstackPortInfoMap.put(portName, portBuilder.build());
openstackPort.securityGroups().stream().forEach(sgId -> {
if (!securityGroupMap.containsKey(sgId)) {
securityGroupMap.put(sgId, openstackService.getSecurityGroup(sgId));
}
});
openstackPortInfoMap.putIfAbsent(port.annotations().value(PORTNAME),
portBuilder.build());
}
private void processHostRemoved(Host host) {
......
......@@ -58,7 +58,6 @@ public class OpenstackSwitchingRulePopulator {
private static Logger log = LoggerFactory
.getLogger(OpenstackSwitchingRulePopulator.class);
private static final int SWITCHING_RULE_PRIORITY = 30000;
private static final int EAST_WEST_ROUTING_RULE_PRIORITY = 29000;
private static final int TUNNELTAG_RULE_PRIORITY = 30000;
private FlowObjectiveService flowObjectiveService;
......@@ -490,4 +489,5 @@ public class OpenstackSwitchingRulePopulator {
}
return port.number();
}
}
......
......@@ -84,6 +84,23 @@ public class OpenstackPortWebResource extends AbstractWebResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updatePorts(InputStream input) {
try {
ObjectMapper mapper = new ObjectMapper();
ObjectNode portNode = (ObjectNode) mapper.readTree(input);
OpenstackPort openstackPort = PORT_CODEC.decode(portNode, this);
OpenstackSwitchingService switchingService =
getService(OpenstackSwitchingService.class);
switchingService.updatePort(openstackPort);
log.debug("REST API update port is called with {}", portNode.toString());
return Response.status(Response.Status.OK).build();
} catch (Exception e) {
log.error("Update Port failed because of exception {}",
e.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
.build();
}
}
}
......
......@@ -64,6 +64,7 @@ public class OpenstackPipeline extends DefaultSingleTablePipeline
protected static final int VNI_TABLE = 0;
protected static final int FORWARDING_TABLE = 1;
protected static final int ACL_TABLE = 2;
private static final int DROP_PRIORITY = 0;
private static final int TIME_OUT = 0;
......@@ -136,6 +137,7 @@ public class OpenstackPipeline extends DefaultSingleTablePipeline
private void initializePipeline() {
processVniTable(true);
processForwardingTable(true);
processAclTable(true);
}
private void processVniTable(boolean install) {
......@@ -176,6 +178,26 @@ public class OpenstackPipeline extends DefaultSingleTablePipeline
applyRules(install, flowRule);
}
private void processAclTable(boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.wipeDeferred();
treatment.drop();
FlowRule flowRule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(DROP_PRIORITY)
.fromApp(appId)
.makePermanent()
.forTable(ACL_TABLE)
.build();
applyRules(install, flowRule);
}
private void applyRules(boolean install, FlowRule flowRule) {
FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
......@@ -264,8 +286,15 @@ public class OpenstackPipeline extends DefaultSingleTablePipeline
tBuilder.transition(FORWARDING_TABLE);
ruleBuilder.withTreatment(tBuilder.build());
ruleBuilder.forTable(VNI_TABLE);
} else {
} else if (forwardingObjective.selector().getCriterion(Criterion.Type.TUNNEL_ID) != null) {
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
tBuilder.deferred();
forwardingObjective.treatment().allInstructions().forEach(tBuilder::add);
tBuilder.transition(ACL_TABLE);
ruleBuilder.withTreatment(tBuilder.build());
ruleBuilder.forTable(FORWARDING_TABLE);
} else {
ruleBuilder.forTable(ACL_TABLE);
}
return Collections.singletonList(ruleBuilder.build());
......