Madan Jampani
Committed by Gerrit Code Review

Fixes deadlock in dhcp ip assignment logic caused by nesting calls to distributed primitives.

Change-Id: I25acd37cbf3800ad260c672c99e9f630435f0f88
...@@ -25,13 +25,11 @@ import org.apache.felix.scr.annotations.Service; ...@@ -25,13 +25,11 @@ import org.apache.felix.scr.annotations.Service;
25 import org.onlab.packet.Ip4Address; 25 import org.onlab.packet.Ip4Address;
26 import org.onlab.packet.MacAddress; 26 import org.onlab.packet.MacAddress;
27 import org.onlab.util.KryoNamespace; 27 import org.onlab.util.KryoNamespace;
28 -import org.onlab.util.Tools;
29 import org.onosproject.dhcp.DhcpStore; 28 import org.onosproject.dhcp.DhcpStore;
30 import org.onosproject.dhcp.IpAssignment; 29 import org.onosproject.dhcp.IpAssignment;
31 import org.onosproject.net.HostId; 30 import org.onosproject.net.HostId;
32 import org.onosproject.store.serializers.KryoNamespaces; 31 import org.onosproject.store.serializers.KryoNamespaces;
33 import org.onosproject.store.service.ConsistentMap; 32 import org.onosproject.store.service.ConsistentMap;
34 -import org.onosproject.store.service.ConsistentMapException;
35 import org.onosproject.store.service.DistributedSet; 33 import org.onosproject.store.service.DistributedSet;
36 import org.onosproject.store.service.Serializer; 34 import org.onosproject.store.service.Serializer;
37 import org.onosproject.store.service.StorageService; 35 import org.onosproject.store.service.StorageService;
...@@ -44,7 +42,6 @@ import java.util.Map; ...@@ -44,7 +42,6 @@ import java.util.Map;
44 import java.util.List; 42 import java.util.List;
45 import java.util.HashMap; 43 import java.util.HashMap;
46 import java.util.Objects; 44 import java.util.Objects;
47 -import java.util.concurrent.atomic.AtomicBoolean;
48 45
49 /** 46 /**
50 * Manages the pool of available IP Addresses in the network and 47 * Manages the pool of available IP Addresses in the network and
...@@ -172,83 +169,79 @@ public class DistributedDhcpStore implements DhcpStore { ...@@ -172,83 +169,79 @@ public class DistributedDhcpStore implements DhcpStore {
172 List<Ip4Address> addressList) { 169 List<Ip4Address> addressList) {
173 log.debug("Assign IP Called w/ Ip4Address: {}, HostId: {}", ipAddr.toString(), hostId.mac().toString()); 170 log.debug("Assign IP Called w/ Ip4Address: {}, HostId: {}", ipAddr.toString(), hostId.mac().toString());
174 171
175 - AtomicBoolean assigned = Tools.retryable(() -> { 172 + Versioned<IpAssignment> currentAssignment = allocationMap.get(hostId);
176 - AtomicBoolean result = new AtomicBoolean(false); 173 + IpAssignment newAssignment = null;
177 - allocationMap.compute( 174 + if (currentAssignment == null) {
178 - hostId, 175 + if (rangeNotEnforced) {
179 - (h, existingAssignment) -> { 176 + newAssignment = IpAssignment.builder()
180 - IpAssignment assignment = existingAssignment; 177 + .ipAddress(ipAddr)
181 - if (existingAssignment == null) { 178 + .timestamp(new Date())
182 - if (rangeNotEnforced) { 179 + .leasePeriod(leaseTime)
183 - assignment = IpAssignment.builder() 180 + .rangeNotEnforced(true)
184 - .ipAddress(ipAddr) 181 + .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
185 - .timestamp(new Date()) 182 + .subnetMask((Ip4Address) addressList.toArray()[0])
186 - .leasePeriod(leaseTime) 183 + .dhcpServer((Ip4Address) addressList.toArray()[1])
187 - .rangeNotEnforced(true) 184 + .routerAddress((Ip4Address) addressList.toArray()[2])
188 - .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced) 185 + .domainServer((Ip4Address) addressList.toArray()[3])
189 - .subnetMask((Ip4Address) addressList.toArray()[0]) 186 + .build();
190 - .dhcpServer((Ip4Address) addressList.toArray()[1])
191 - .routerAddress((Ip4Address) addressList.toArray()[2])
192 - .domainServer((Ip4Address) addressList.toArray()[3])
193 - .build();
194 - result.set(true);
195 - } else if (freeIPPool.remove(ipAddr)) {
196 - assignment = IpAssignment.builder()
197 - .ipAddress(ipAddr)
198 - .timestamp(new Date())
199 - .leasePeriod(leaseTime)
200 - .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
201 - .build();
202 - result.set(true);
203 - }
204 - } else if (Objects.equals(existingAssignment.ipAddress(), ipAddr) &&
205 - (existingAssignment.rangeNotEnforced() || ipWithinRange(ipAddr))) {
206 - switch (existingAssignment.assignmentStatus()) {
207 - case Option_RangeNotEnforced:
208 - assignment = IpAssignment.builder()
209 - .ipAddress(ipAddr)
210 - .timestamp(new Date())
211 - .leasePeriod(existingAssignment.leasePeriod())
212 - .rangeNotEnforced(true)
213 - .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
214 - .subnetMask(existingAssignment.subnetMask())
215 - .dhcpServer(existingAssignment.dhcpServer())
216 - .routerAddress(existingAssignment.routerAddress())
217 - .domainServer(existingAssignment.domainServer())
218 - .build();
219 - result.set(true);
220 - break;
221 - case Option_Assigned:
222 - case Option_Requested:
223 - assignment = IpAssignment.builder()
224 - .ipAddress(ipAddr)
225 - .timestamp(new Date())
226 - .leasePeriod(leaseTime)
227 - .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
228 - .build();
229 - result.set(true);
230 - break;
231 - case Option_Expired:
232 - if (freeIPPool.remove(ipAddr)) {
233 - assignment = IpAssignment.builder()
234 - .ipAddress(ipAddr)
235 - .timestamp(new Date())
236 - .leasePeriod(leaseTime)
237 - .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
238 - .build();
239 - result.set(true);
240 - }
241 - break;
242 - default:
243 - break;
244 - }
245 - }
246 - return assignment;
247 - });
248 - return result;
249 - }, ConsistentMapException.class, MAX_RETRIES, MAX_BACKOFF).get();
250 187
251 - return assigned.get(); 188 + } else if (freeIPPool.remove(ipAddr)) {
189 + newAssignment = IpAssignment.builder()
190 + .ipAddress(ipAddr)
191 + .timestamp(new Date())
192 + .leasePeriod(leaseTime)
193 + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
194 + .build();
195 + } else {
196 + return false;
197 + }
198 + return allocationMap.putIfAbsent(hostId, newAssignment) == null;
199 + // TODO: handle the case where map changed.
200 + } else {
201 + IpAssignment existingAssignment = currentAssignment.value();
202 + if (Objects.equals(existingAssignment.ipAddress(), ipAddr) &&
203 + (existingAssignment.rangeNotEnforced() || ipWithinRange(ipAddr))) {
204 + switch (existingAssignment.assignmentStatus()) {
205 + case Option_RangeNotEnforced:
206 + newAssignment = IpAssignment.builder()
207 + .ipAddress(ipAddr)
208 + .timestamp(new Date())
209 + .leasePeriod(existingAssignment.leasePeriod())
210 + .rangeNotEnforced(true)
211 + .assignmentStatus(IpAssignment.AssignmentStatus.Option_RangeNotEnforced)
212 + .subnetMask(existingAssignment.subnetMask())
213 + .dhcpServer(existingAssignment.dhcpServer())
214 + .routerAddress(existingAssignment.routerAddress())
215 + .domainServer(existingAssignment.domainServer())
216 + .build();
217 + break;
218 + case Option_Assigned:
219 + case Option_Requested:
220 + newAssignment = IpAssignment.builder()
221 + .ipAddress(ipAddr)
222 + .timestamp(new Date())
223 + .leasePeriod(leaseTime)
224 + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
225 + .build();
226 + break;
227 + case Option_Expired:
228 + if (freeIPPool.remove(ipAddr)) {
229 + newAssignment = IpAssignment.builder()
230 + .ipAddress(ipAddr)
231 + .timestamp(new Date())
232 + .leasePeriod(leaseTime)
233 + .assignmentStatus(IpAssignment.AssignmentStatus.Option_Assigned)
234 + .build();
235 + }
236 + break;
237 + default:
238 + break;
239 + }
240 + return allocationMap.replace(hostId, currentAssignment.version(), newAssignment);
241 + } else {
242 + return false;
243 + }
244 + }
252 } 245 }
253 246
254 @Override 247 @Override
......