daniel
Committed by Gerrit Code Review

[CORD 304] Modification of DhcpService to support OpenStack

- Now DhcpService can support DHCP requests from openstack

Change-Id: I30b51510290fe30b5f3bceb676e1d20bf8e33611
......@@ -19,8 +19,10 @@ import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.net.HostId;
import java.util.List;
import java.util.Map;
/**
* DHCP Service Interface.
*/
......@@ -56,12 +58,16 @@ public interface DhcpService {
/**
* Registers a static IP mapping with the DHCP Server.
* Supports the request from OpenStack
*
* @param macID macID of the client
* @param macID macID of the client
* @param ipAddress IP Address requested for the client
* @return true if the mapping was successfully registered, false otherwise
* @param fromOpenStack true if the request is from OpenStack
* @param addressList subnetMask, DHCP/Router/Domain Server IP Address if the request from OpenStack
* @return true if the mapping was successfully added, false otherwise
*/
boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress);
boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress, boolean fromOpenStack,
List<Ip4Address> addressList);
/**
* Removes a static IP mapping with the DHCP Server.
......@@ -77,5 +83,4 @@ public interface DhcpService {
* @return list of available IPs
*/
Iterable<Ip4Address> getAvailableIPs();
}
......
......@@ -19,8 +19,10 @@ import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.net.HostId;
import java.util.List;
import java.util.Map;
/**
* DHCPStore Interface.
*/
......@@ -43,15 +45,21 @@ public interface DhcpStore {
*/
Ip4Address suggestIP(HostId hostId, Ip4Address requestedIP);
/**
* Assigns the requested IP to the Mac ID, in response to a DHCP REQUEST message.
*
* @param hostId Host Id of the client requesting an IP
* @param ipAddr IP Address being requested
* @param leaseTime Lease time offered by the server for this mapping
* @param fromOpenStack true if the request is from Openstack
* @param addressList subnetMask, DHCP IP Address, Router IP Address, Domain Server IP Address if the request
* from OpenStack
* @return returns true if the assignment was successful, false otherwise
*/
boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime);
boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean fromOpenStack,
List<Ip4Address> addressList);
/**
* Sets the default time for which suggested IP mappings are valid.
......@@ -87,9 +95,11 @@ public interface DhcpStore {
*
* @param macID macID of the client
* @param ipAddr IP Address requested for the client
* @param fromOpenStack true if the request is from Openstack
* @param addressList subnetMask, DHCP/Router/Domain Server IP Address if the request from OpenStack
* @return true if the mapping was successfully registered, false otherwise
*/
boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr);
boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean fromOpenStack, List<Ip4Address> addressList);
/**
* Removes a static IP mapping associated with the given MAC ID from the DHCP Server.
......@@ -106,4 +116,11 @@ public interface DhcpStore {
*/
Iterable<Ip4Address> getAvailableIPs();
/**
*
*
* @param hostId
* @return
*/
IpAssignment getIpAssignmentFromAllocationMap(HostId hostId);
}
......
......@@ -33,6 +33,16 @@ public final class IpAssignment {
private final long leasePeriod;
private final Ip4Address subnetMask;
private final Ip4Address dhcpServer;
private final Ip4Address routerAddress;
private final Ip4Address domainServer;
private final boolean fromOpenStack;
private final AssignmentStatus assignmentStatus;
public enum AssignmentStatus {
......@@ -42,6 +52,10 @@ public final class IpAssignment {
Option_Requested,
/**
* IP Assignment has been requested by a OpenStack.
*/
Option_Requested_From_OpenStack,
/**
* IP has been assigned to a host.
*/
Option_Assigned,
......@@ -58,16 +72,28 @@ public final class IpAssignment {
*
* @param ipAddress
* @param leasePeriod
* @param timestamp
* @param assignmentStatus
* @param subnetMask
* @param dhcpServer
* @param routerAddress
* @param domainServer
* @param fromOpenStack
*/
private IpAssignment(Ip4Address ipAddress,
long leasePeriod,
Date timestamp,
AssignmentStatus assignmentStatus) {
AssignmentStatus assignmentStatus, Ip4Address subnetMask, Ip4Address dhcpServer,
Ip4Address routerAddress, Ip4Address domainServer, boolean fromOpenStack) {
this.ipAddress = ipAddress;
this.leasePeriod = leasePeriod;
this.timestamp = timestamp;
this.assignmentStatus = assignmentStatus;
this.subnetMask = subnetMask;
this.dhcpServer = dhcpServer;
this.routerAddress = routerAddress;
this.domainServer = domainServer;
this.fromOpenStack = fromOpenStack;
}
/**
......@@ -115,6 +141,26 @@ public final class IpAssignment {
return (int) this.leasePeriod * 1000;
}
public Ip4Address subnetMask() {
return subnetMask;
}
public Ip4Address dhcpServer() {
return dhcpServer;
}
public Ip4Address routerAddress() {
return routerAddress;
}
public Ip4Address domainServer() {
return domainServer;
}
public boolean fromOpenStack() {
return fromOpenStack;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
......@@ -122,6 +168,11 @@ public final class IpAssignment {
.add("timestamp", timestamp)
.add("lease", leasePeriod)
.add("assignmentStatus", assignmentStatus)
.add("subnetMask", subnetMask)
.add("dhcpServer", dhcpServer)
.add("routerAddress", routerAddress)
.add("domainServer", domainServer)
.add("fromOpenStack", fromOpenStack)
.toString();
}
......@@ -157,6 +208,16 @@ public final class IpAssignment {
private AssignmentStatus assignmentStatus;
private Ip4Address subnetMask;
private Ip4Address dhcpServer;
private Ip4Address domainServer;
private Ip4Address routerAddress;
private boolean fromOpenStack = false;
private Builder() {
}
......@@ -170,10 +231,8 @@ public final class IpAssignment {
public IpAssignment build() {
validateInputs();
return new IpAssignment(ipAddress,
leasePeriod,
timeStamp,
assignmentStatus);
return new IpAssignment(ipAddress, leasePeriod, timeStamp, assignmentStatus, subnetMask,
dhcpServer, domainServer, routerAddress, fromOpenStack);
}
public Builder ipAddress(Ip4Address addr) {
......@@ -196,14 +255,48 @@ public final class IpAssignment {
return this;
}
public Builder subnetMask(Ip4Address subnetMask) {
this.subnetMask = subnetMask;
return this;
}
public Builder dhcpServer(Ip4Address dhcpServer) {
this.dhcpServer = dhcpServer;
return this;
}
public Builder domainServer(Ip4Address domainServer) {
this.domainServer = domainServer;
return this;
}
public Builder routerAddress(Ip4Address routerAddress) {
this.routerAddress = routerAddress;
return this;
}
public Builder fromOpenStack(boolean fromOpenStack) {
this.fromOpenStack = fromOpenStack;
return this;
}
private void validateInputs() {
checkNotNull(ipAddress, "IP Address must be specified");
checkNotNull(assignmentStatus, "Assignment Status must be specified");
checkNotNull(leasePeriod, "Lease Period must be specified");
checkNotNull(timeStamp, "Timestamp must be specified");
if (fromOpenStack) {
checkNotNull(subnetMask, "subnetMask must be specified in case of OpenStack");
checkNotNull(dhcpServer, "dhcpServer must be specified in case of OpenStack");
checkNotNull(domainServer, "domainServer must be specified in case of OpenStack");
checkNotNull(routerAddress, "routerAddress must be specified in case of OpenStack");
}
switch (assignmentStatus) {
case Option_Requested:
case Option_Requested_From_OpenStack:
case Option_Assigned:
case Option_Expired:
break;
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.dhcp.cli;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.packet.Ip4Address;
......@@ -48,7 +49,7 @@ public class DhcpSetStaticMapping extends AbstractShellCommand {
try {
MacAddress macID = MacAddress.valueOf(macAddr);
Ip4Address ipAddress = Ip4Address.valueOf(ipAddr);
if (dhcpService.setStaticMapping(macID, ipAddress)) {
if (dhcpService.setStaticMapping(macID, ipAddress, false, Lists.newArrayList())) {
print(DHCP_SUCCESS);
} else {
print(DHCP_FAILURE);
......
......@@ -16,6 +16,7 @@
package org.onosproject.dhcp.impl;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -77,7 +78,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.onlab.packet.MacAddress.valueOf;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
......@@ -168,7 +168,6 @@ public class DhcpManager implements DhcpService {
cfgService.addListener(cfgListener);
factories.forEach(cfgService::registerConfigFactory);
cfgListener.reconfigureNetwork(cfgService.getConfig(appId, DhcpConfig.class));
hostProviderService = hostProviderRegistry.register(hostProvider);
packetService.addProcessor(processor, PacketProcessor.director(0));
requestPackets();
......@@ -242,8 +241,12 @@ public class DhcpManager implements DhcpService {
}
@Override
public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress) {
return dhcpStore.assignStaticIP(macID, ipAddress);
public boolean setStaticMapping(MacAddress macID, Ip4Address ipAddress, boolean fromOpenStack,
List<Ip4Address> addressList) {
log.debug("setStaticMapping is called with Mac: {}, Ip: {} addressList: {}",
macID.toString(), ipAddress.toString(), addressList.toString());
return dhcpStore.assignStaticIP(macID, ipAddress, fromOpenStack, addressList);
}
@Override
......@@ -268,6 +271,26 @@ public class DhcpManager implements DhcpService {
*/
private Ethernet buildReply(Ethernet packet, Ip4Address ipOffered, byte outgoingMessageType) {
Ip4Address subnetMaskReply;
Ip4Address dhcpServerReply;
Ip4Address routerAddressReply;
Ip4Address domainServerReply;
IpAssignment ipAssignment;
ipAssignment = dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(packet.getSourceMAC()));
if (ipAssignment != null && ipAssignment.fromOpenStack()) {
subnetMaskReply = ipAssignment.subnetMask();
dhcpServerReply = ipAssignment.dhcpServer();
domainServerReply = ipAssignment.domainServer();
routerAddressReply = ipAssignment.routerAddress();
} else {
subnetMaskReply = subnetMask;
dhcpServerReply = myIP;
routerAddressReply = routerAddress;
domainServerReply = domainServer;
}
// Ethernet Frame.
Ethernet ethReply = new Ethernet();
ethReply.setSourceMACAddress(myMAC);
......@@ -278,7 +301,7 @@ public class DhcpManager implements DhcpService {
// IP Packet
IPv4 ipv4Packet = (IPv4) packet.getPayload();
IPv4 ipv4Reply = new IPv4();
ipv4Reply.setSourceAddress(myIP.toInt());
ipv4Reply.setSourceAddress(dhcpServerReply.toInt());
ipv4Reply.setDestinationAddress(ipOffered.toInt());
ipv4Reply.setTtl(packetTTL);
......@@ -299,7 +322,7 @@ public class DhcpManager implements DhcpService {
if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
dhcpReply.setYourIPAddress(ipOffered.toInt());
dhcpReply.setServerIPAddress(myIP.toInt());
dhcpReply.setServerIPAddress(dhcpServerReply.toInt());
if (dhcpPacket.getGatewayIPAddress() == 0) {
ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt());
}
......@@ -322,7 +345,7 @@ public class DhcpManager implements DhcpService {
option = new DHCPOption();
option.setCode(DHCP.DHCPOptionCode.OptionCode_DHCPServerIp.getValue());
option.setLength((byte) 4);
option.setData(myIP.toOctets());
option.setData(dhcpServerReply.toOctets());
optionList.add(option);
if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) {
......@@ -352,7 +375,7 @@ public class DhcpManager implements DhcpService {
option = new DHCPOption();
option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue());
option.setLength((byte) 4);
option.setData(subnetMask.toOctets());
option.setData(subnetMaskReply.toOctets());
optionList.add(option);
// Broadcast Address.
......@@ -366,14 +389,14 @@ public class DhcpManager implements DhcpService {
option = new DHCPOption();
option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue());
option.setLength((byte) 4);
option.setData(routerAddress.toOctets());
option.setData(routerAddressReply.toOctets());
optionList.add(option);
// DNS Server Address.
option = new DHCPOption();
option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue());
option.setLength((byte) 4);
option.setData(domainServer.toOctets());
option.setData(domainServerReply.toOctets());
optionList.add(option);
}
......@@ -384,7 +407,6 @@ public class DhcpManager implements DhcpService {
optionList.add(option);
dhcpReply.setOptions(optionList);
udpReply.setPayload(dhcpReply);
ipv4Reply.setPayload(udpReply);
ethReply.setPayload(ipv4Reply);
......@@ -449,31 +471,40 @@ public class DhcpManager implements DhcpService {
if (incomingPacketType.getValue() == DHCPPacketType.DHCPDISCOVER.getValue()) {
outgoingPacketType = DHCPPacketType.DHCPOFFER;
Ip4Address ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
Ip4Address ipOffered = null;
ipOffered = dhcpStore.suggestIP(hostId, requestedIP);
if (ipOffered != null) {
Ethernet ethReply = buildReply(packet, ipOffered,
(byte) outgoingPacketType.getValue());
sendReply(context, ethReply);
}
} else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) {
if (flagIfServerIP && flagIfRequestedIP) {
// SELECTING state
if (myIP.equals(serverIP)) {
if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
outgoingPacketType = DHCPPacketType.DHCPACK;
discoverHost(context, requestedIP);
} else {
outgoingPacketType = DHCPPacketType.DHCPNAK;
}
if (dhcpStore.getIpAssignmentFromAllocationMap(HostId.hostId(clientMAC))
.fromOpenStack()) {
outgoingPacketType = DHCPPacketType.DHCPACK;
Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
sendReply(context, ethReply);
} else {
if (myIP.equals(serverIP)) {
if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
outgoingPacketType = DHCPPacketType.DHCPACK;
discoverHost(context, requestedIP);
} else {
outgoingPacketType = DHCPPacketType.DHCPNAK;
}
Ethernet ethReply = buildReply(packet, requestedIP,
(byte) outgoingPacketType.getValue());
sendReply(context, ethReply);
}
}
} else if (flagIfRequestedIP) {
// INIT-REBOOT state
if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) {
if (dhcpStore.assignIP(hostId, requestedIP, leaseTime, false, Lists.newArrayList())) {
outgoingPacketType = DHCPPacketType.DHCPACK;
Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue());
sendReply(context, ethReply);
......@@ -485,7 +516,7 @@ public class DhcpManager implements DhcpService {
int ciaadr = dhcpPayload.getClientIPAddress();
if (ciaadr != 0) {
Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr);
if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime)) {
if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime, false, Lists.newArrayList())) {
outgoingPacketType = DHCPPacketType.DHCPACK;
discoverHost(context, clientIaddr);
} else if (packet.getEtherType() == Ethernet.TYPE_IPV4 &&
......
......@@ -38,8 +38,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.HashMap;
import java.util.Objects;
/**
......@@ -105,7 +106,9 @@ public class DistributedDhcpStore implements DhcpStore {
IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
Ip4Address ipAddr = assignmentInfo.ipAddress();
if (status == IpAssignment.AssignmentStatus.Option_Assigned ||
if (assignmentInfo.fromOpenStack()) {
return assignmentInfo.ipAddress();
} else if (status == IpAssignment.AssignmentStatus.Option_Assigned ||
status == IpAssignment.AssignmentStatus.Option_Requested) {
// Client has a currently Active Binding.
if (ipWithinRange(ipAddr)) {
......@@ -160,9 +163,11 @@ public class DistributedDhcpStore implements DhcpStore {
}
@Override
public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime) {
public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean fromOpenStack,
List<Ip4Address> addressList) {
IpAssignment assignmentInfo;
if (allocationMap.containsKey(hostId)) {
assignmentInfo = allocationMap.get(hostId).value();
IpAssignment.AssignmentStatus status = assignmentInfo.assignmentStatus();
......@@ -207,6 +212,20 @@ public class DistributedDhcpStore implements DhcpStore {
allocationMap.put(hostId, assignmentInfo);
return true;
}
} else if (fromOpenStack) {
assignmentInfo = IpAssignment.builder()
.ipAddress(ipAddr)
.timestamp(new Date())
.leasePeriod(leaseTime)
.fromOpenStack(true)
.assignmentStatus(IpAssignment.AssignmentStatus.Option_Requested_From_OpenStack)
.subnetMask((Ip4Address) addressList.toArray()[0])
.dhcpServer((Ip4Address) addressList.toArray()[1])
.domainServer((Ip4Address) addressList.toArray()[2])
.routerAddress((Ip4Address) addressList.toArray()[3])
.build();
allocationMap.put(hostId, assignmentInfo);
return true;
}
return false;
}
......@@ -239,7 +258,8 @@ public class DistributedDhcpStore implements DhcpStore {
IpAssignment assignment;
for (Map.Entry<HostId, Versioned<IpAssignment>> entry: allocationMap.entrySet()) {
assignment = entry.getValue().value();
if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned) {
if (assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Assigned
|| assignment.assignmentStatus() == IpAssignment.AssignmentStatus.Option_Requested_From_OpenStack) {
validMapping.put(entry.getKey(), assignment);
}
}
......@@ -256,9 +276,10 @@ public class DistributedDhcpStore implements DhcpStore {
}
@Override
public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) {
public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean fromOpenStack,
List<Ip4Address> addressList) {
HostId host = HostId.hostId(macID);
return assignIP(host, ipAddr, -1);
return assignIP(host, ipAddr, -1, fromOpenStack, addressList);
}
@Override
......@@ -299,6 +320,11 @@ public class DistributedDhcpStore implements DhcpStore {
}
}
@Override
public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
return allocationMap.get(hostId).value();
}
/**
* Fetches the next available IP from the free pool pf IPs.
*
......@@ -326,3 +352,4 @@ public class DistributedDhcpStore implements DhcpStore {
return false;
}
}
......
......@@ -18,6 +18,7 @@ package org.onosproject.dhcp.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.dhcp.DhcpService;
......@@ -121,7 +122,7 @@ public class DHCPWebResource extends AbstractWebResource {
if (macID != null && ip != null) {
if (!service.setStaticMapping(MacAddress.valueOf(macID.asText()),
Ip4Address.valueOf(ip.asText()))) {
Ip4Address.valueOf(ip.asText()), false, Lists.newArrayList())) {
throw new IllegalArgumentException("Static Mapping Failed. The IP maybe unavailable.");
}
}
......
......@@ -228,7 +228,8 @@ public class DhcpManagerTest {
return Ip4Address.valueOf(EXPECTED_IP);
}
public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime) {
public boolean assignIP(HostId hostId, Ip4Address ipAddr, int leaseTime, boolean fromOpenStack,
List<Ip4Address> addressList) {
return true;
}
......@@ -255,7 +256,8 @@ public class DhcpManagerTest {
return map;
}
public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr) {
public boolean assignStaticIP(MacAddress macID, Ip4Address ipAddr, boolean fromOpenStack,
List<Ip4Address> addressList) {
return true;
}
......@@ -268,6 +270,9 @@ public class DhcpManagerTest {
ipList.add(Ip4Address.valueOf(EXPECTED_IP));
return ImmutableSet.copyOf(ipList);
}
public IpAssignment getIpAssignmentFromAllocationMap(HostId hostId) {
return null;
}
}
/**
......