Merge "Fix for Bug 2290."
[controller.git] / opendaylight / networkconfiguration / neutron / src / main / java / org / opendaylight / controller / networkconfig / neutron / NeutronSubnet.java
1 /*
2  * Copyright IBM Corporation and others, 2013.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.networkconfig.neutron;
10
11 import java.io.Serializable;
12 import java.net.InetAddress;
13 import java.net.Inet6Address;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
17
18 import javax.xml.bind.annotation.XmlAccessType;
19 import javax.xml.bind.annotation.XmlAccessorType;
20 import javax.xml.bind.annotation.XmlElement;
21 import javax.xml.bind.annotation.XmlRootElement;
22
23 import org.apache.commons.net.util.SubnetUtils;
24 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
25 import org.opendaylight.controller.configuration.ConfigurationObject;
26
27 @XmlRootElement
28 @XmlAccessorType(XmlAccessType.NONE)
29
30 public class NeutronSubnet extends ConfigurationObject implements Serializable, INeutronObject {
31     private static final long serialVersionUID = 1L;
32
33     // See OpenStack Network API v2.0 Reference for description of
34     // annotated attributes
35
36     @XmlElement (name="id")
37     String subnetUUID;
38
39     @XmlElement (name="network_id")
40     String networkUUID;
41
42     @XmlElement (name="name")
43     String name;
44
45     @XmlElement (defaultValue="4", name="ip_version")
46     Integer ipVersion;
47
48     @XmlElement (name="cidr")
49     String cidr;
50
51     @XmlElement (name="gateway_ip")
52     String gatewayIP;
53
54     @XmlElement (name="dns_nameservers")
55     List<String> dnsNameservers;
56
57     @XmlElement (name="allocation_pools")
58     List<NeutronSubnet_IPAllocationPool> allocationPools;
59
60     @XmlElement (name="host_routes")
61     List<NeutronSubnet_HostRoute> hostRoutes;
62
63     @XmlElement (defaultValue="true", name="enable_dhcp")
64     Boolean enableDHCP;
65
66     @XmlElement (name="tenant_id")
67     String tenantID;
68
69     @XmlElement (name="ipv6_address_mode", nillable=true)
70     String ipV6AddressMode;
71
72     @XmlElement (name="ipv6_ra_mode", nillable=true)
73     String ipV6RaMode;
74
75     /* stores the OpenStackPorts associated with an instance
76      * used to determine if that instance can be deleted.
77      */
78     List<NeutronPort> myPorts;
79
80     Boolean gatewayIPAssigned;
81
82     public NeutronSubnet() {
83         myPorts = new ArrayList<NeutronPort>();
84     }
85
86     public String getID() { return subnetUUID; }
87
88     public void setID(String id) { this.subnetUUID = id; }
89
90     public String getSubnetUUID() {
91         return subnetUUID;
92     }
93
94     public void setSubnetUUID(String subnetUUID) {
95         this.subnetUUID = subnetUUID;
96     }
97
98     public String getNetworkUUID() {
99         return networkUUID;
100     }
101
102     public void setNetworkUUID(String networkUUID) {
103         this.networkUUID = networkUUID;
104     }
105
106     public String getName() {
107         return name;
108     }
109
110     public void setName(String name) {
111         this.name = name;
112     }
113
114     public Integer getIpVersion() {
115         return ipVersion;
116     }
117
118     public void setIpVersion(Integer ipVersion) {
119         this.ipVersion = ipVersion;
120     }
121
122     public String getCidr() {
123         return cidr;
124     }
125
126     public void setCidr(String cidr) {
127         this.cidr = cidr;
128     }
129
130     public String getGatewayIP() {
131         return gatewayIP;
132     }
133
134     public void setGatewayIP(String gatewayIP) {
135         this.gatewayIP = gatewayIP;
136     }
137
138     public List<String> getDnsNameservers() {
139         return dnsNameservers;
140     }
141
142     public void setDnsNameservers(List<String> dnsNameservers) {
143         this.dnsNameservers = dnsNameservers;
144     }
145
146     public List<NeutronSubnet_IPAllocationPool> getAllocationPools() {
147         return allocationPools;
148     }
149
150     public void setAllocationPools(List<NeutronSubnet_IPAllocationPool> allocationPools) {
151         this.allocationPools = allocationPools;
152     }
153
154     public List<NeutronSubnet_HostRoute> getHostRoutes() {
155         return hostRoutes;
156     }
157
158     public void setHostRoutes(List<NeutronSubnet_HostRoute> hostRoutes) {
159         this.hostRoutes = hostRoutes;
160     }
161
162     public boolean isEnableDHCP() {
163         if (enableDHCP == null) {
164             return true;
165         }
166         return enableDHCP;
167     }
168
169     public Boolean getEnableDHCP() { return enableDHCP; }
170
171     public void setEnableDHCP(Boolean newValue) {
172             enableDHCP = newValue;
173     }
174
175     public String getTenantID() {
176         return tenantID;
177     }
178
179     public void setTenantID(String tenantID) {
180         this.tenantID = tenantID;
181     }
182
183     public String getIpV6AddressMode() { return ipV6AddressMode; }
184
185     public void setIpV6AddressMode(String ipV6AddressMode) { this.ipV6AddressMode = ipV6AddressMode; }
186
187     public String getIpV6RaMode() { return ipV6RaMode; }
188
189     public void setIpV6RaMode(String ipV6RaMode) { this.ipV6RaMode = ipV6RaMode; }
190
191     /**
192      * This method copies selected fields from the object and returns them
193      * as a new object, suitable for marshaling.
194      *
195      * @param fields
196      *            List of attributes to be extracted
197      * @return an OpenStackSubnets object with only the selected fields
198      * populated
199      */
200
201     public NeutronSubnet extractFields(List<String> fields) {
202         NeutronSubnet ans = new NeutronSubnet();
203         Iterator<String> i = fields.iterator();
204         while (i.hasNext()) {
205             String s = i.next();
206             if (s.equals("id")) {
207                 ans.setSubnetUUID(this.getSubnetUUID());
208             }
209             if (s.equals("network_id")) {
210                 ans.setNetworkUUID(this.getNetworkUUID());
211             }
212             if (s.equals("name")) {
213                 ans.setName(this.getName());
214             }
215             if (s.equals("ip_version")) {
216                 ans.setIpVersion(this.getIpVersion());
217             }
218             if (s.equals("cidr")) {
219                 ans.setCidr(this.getCidr());
220             }
221             if (s.equals("gateway_ip")) {
222                 ans.setGatewayIP(this.getGatewayIP());
223             }
224             if (s.equals("dns_nameservers")) {
225                 List<String> nsList = new ArrayList<String>();
226                 nsList.addAll(this.getDnsNameservers());
227                 ans.setDnsNameservers(nsList);
228             }
229             if (s.equals("allocation_pools")) {
230                 List<NeutronSubnet_IPAllocationPool> aPools = new ArrayList<NeutronSubnet_IPAllocationPool>();
231                 aPools.addAll(this.getAllocationPools());
232                 ans.setAllocationPools(aPools);
233             }
234             if (s.equals("host_routes")) {
235                 List<NeutronSubnet_HostRoute> hRoutes = new ArrayList<NeutronSubnet_HostRoute>();
236                 hRoutes.addAll(this.getHostRoutes());
237                 ans.setHostRoutes(hRoutes);
238             }
239             if (s.equals("enable_dhcp")) {
240                 ans.setEnableDHCP(this.getEnableDHCP());
241             }
242             if (s.equals("tenant_id")) {
243                 ans.setTenantID(this.getTenantID());
244             }
245             if (s.equals("ipv6_address_mode")) {
246                 ans.setIpV6AddressMode(this.getIpV6AddressMode());
247             }
248             if (s.equals("ipv6_ra_mode")) {
249                 ans.setIpV6RaMode(this.getIpV6RaMode());
250             }
251         }
252         return ans;
253     }
254
255     /* test to see if the cidr address used to define this subnet
256      * is a valid network address (an necessary condition when creating
257      * a new subnet)
258      */
259     public boolean isValidCIDR() {
260         // fix for Bug 2290 - need to wrap the existing test as
261         // IPv4 because SubnetUtils doesn't support IPv6
262         if (ipVersion == 4) {
263             try {
264                 SubnetUtils util = new SubnetUtils(cidr);
265                 SubnetInfo info = util.getInfo();
266                 if (!info.getNetworkAddress().equals(info.getAddress())) {
267                     return false;
268                 }
269             } catch (Exception e) {
270                 return false;
271             }
272             return true;
273         }
274         if (ipVersion == 6) {
275             // fix for Bug2290 - this is custom code because no classes
276             // with ODL-friendly licenses have been found
277             // extract address (in front of /) and length (after /)
278             String[] parts = cidr.split("/");
279             if (parts.length != 2) {
280                 return false;
281             }
282             try {
283                 int length = Integer.parseInt(parts[1]);
284                 //TODO?: limit check on length
285                 // convert to byte array
286                 byte[] addrBytes = ((Inet6Address) InetAddress.getByName(parts[0])).getAddress();
287                 int i;
288                 for (i=length; i<128; i++) { // offset is to ensure proper comparison
289                     if (((((int) addrBytes[i/8]) & 0x000000FF) & (1 << (7-(i%8)))) != 0) {
290                         return(false);
291                     }
292                 }
293                 return(true);
294             } catch (Exception e) {
295                 return(false);
296             }
297         }
298         return false;
299     }
300
301     /* test to see if the gateway IP specified overlaps with specified
302      * allocation pools (an error condition when creating a new subnet
303      * or assigning a gateway IP)
304      */
305     public boolean gatewayIP_Pool_overlap() {
306         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();
307         while (i.hasNext()) {
308             NeutronSubnet_IPAllocationPool pool = i.next();
309             if (pool.contains(gatewayIP)) {
310                 return true;
311             }
312         }
313         return false;
314     }
315
316     public boolean initDefaults() {
317         if (enableDHCP == null) {
318             enableDHCP = true;
319         }
320         if (ipVersion == null) {
321             ipVersion = 4;
322         }
323         gatewayIPAssigned = false;
324         dnsNameservers = new ArrayList<String>();
325         if (hostRoutes == null) {
326             hostRoutes = new ArrayList<NeutronSubnet_HostRoute>();
327         }
328         if (allocationPools == null) {
329             allocationPools = new ArrayList<NeutronSubnet_IPAllocationPool>();
330             try {
331                 SubnetUtils util = new SubnetUtils(cidr);
332                 SubnetInfo info = util.getInfo();
333                 if (gatewayIP == null || ("").equals(gatewayIP)) {
334                     gatewayIP = info.getLowAddress();
335                 }
336                 if (allocationPools.size() < 1) {
337                     NeutronSubnet_IPAllocationPool source =
338                         new NeutronSubnet_IPAllocationPool(info.getLowAddress(),
339                                 info.getHighAddress());
340                     allocationPools = source.splitPool(gatewayIP);
341                 }
342             } catch (Exception e) {
343                 return false;
344             }
345         }
346         return true;
347     }
348
349     public List<NeutronPort> getPortsInSubnet() {
350         return myPorts;
351     }
352
353     public void addPort(NeutronPort port) {
354         myPorts.add(port);
355     }
356
357     public void removePort(NeutronPort port) {
358         myPorts.remove(port);
359     }
360
361     /* this method tests to see if the supplied IPv4 address
362      * is valid for this subnet or not
363      */
364     public boolean isValidIP(String ipAddress) {
365         try {
366             SubnetUtils util = new SubnetUtils(cidr);
367             SubnetInfo info = util.getInfo();
368             return info.isInRange(ipAddress);
369         } catch (Exception e) {
370             return false;
371         }
372     }
373
374     /* test to see if the supplied IPv4 address is part of one of the
375      * available allocation pools or not
376      */
377     public boolean isIPInUse(String ipAddress) {
378         if (ipAddress.equals(gatewayIP) && !gatewayIPAssigned ) {
379             return false;
380         }
381         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();
382         while (i.hasNext()) {
383             NeutronSubnet_IPAllocationPool pool = i.next();
384             if (pool.contains(ipAddress)) {
385                 return false;
386             }
387         }
388         return true;
389     }
390
391     /* method to get the lowest available address of the subnet.
392      * go through all the allocation pools and keep the lowest of their
393      * low addresses.
394      */
395     public String getLowAddr() {
396         String ans = null;
397         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();
398         while (i.hasNext()) {
399             NeutronSubnet_IPAllocationPool pool = i.next();
400             if (ans == null) {
401                 ans = pool.getPoolStart();
402             }
403             else
404                 if (NeutronSubnet_IPAllocationPool.convert(pool.getPoolStart()) <
405                         NeutronSubnet_IPAllocationPool.convert(ans)) {
406                     ans = pool.getPoolStart();
407                 }
408         }
409         return ans;
410     }
411
412     /*
413      * allocate the parameter address.  Because this uses an iterator to
414      * check the instance's list of allocation pools and we want to modify
415      * pools while the iterator is being used, it is necessary to
416      * build a new list of allocation pools and replace the list when
417      * finished (otherwise a split will cause undefined iterator behavior.
418      */
419     public void allocateIP(String ipAddress) {
420         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();
421         List<NeutronSubnet_IPAllocationPool> newList = new ArrayList<NeutronSubnet_IPAllocationPool>();    // we have to modify a separate list
422         while (i.hasNext()) {
423             NeutronSubnet_IPAllocationPool pool = i.next();
424             /* if the pool contains a single address element and we are allocating it
425              * then we don't need to copy the pool over.  Otherwise, we need to possibly
426              * split the pool and add both pieces to the new list
427              */
428             if (!(pool.getPoolEnd().equalsIgnoreCase(ipAddress) &&
429                     pool.getPoolStart().equalsIgnoreCase(ipAddress))) {
430                 if (pool.contains(ipAddress)) {
431                     List<NeutronSubnet_IPAllocationPool> pools = pool.splitPool(ipAddress);
432                     newList.addAll(pools);
433                 } else {
434                     newList.add(pool);
435                 }
436             }
437         }
438         allocationPools = newList;
439     }
440
441     /*
442      * release an IP address back to the subnet.  Although an iterator
443      * is used, the list is not modified until the iterator is complete, so
444      * an extra list is not necessary.
445      */
446     public void releaseIP(String ipAddress) {
447         NeutronSubnet_IPAllocationPool lPool = null;
448         NeutronSubnet_IPAllocationPool hPool = null;
449         Iterator<NeutronSubnet_IPAllocationPool> i = allocationPools.iterator();
450         long sIP = NeutronSubnet_IPAllocationPool.convert(ipAddress);
451         //look for lPool where ipAddr - 1 is high address
452         //look for hPool where ipAddr + 1 is low address
453         while (i.hasNext()) {
454             NeutronSubnet_IPAllocationPool pool = i.next();
455             long lIP = NeutronSubnet_IPAllocationPool.convert(pool.getPoolStart());
456             long hIP = NeutronSubnet_IPAllocationPool.convert(pool.getPoolEnd());
457             if (sIP+1 == lIP) {
458                 hPool = pool;
459             }
460             if (sIP-1 == hIP) {
461                 lPool = pool;
462             }
463         }
464         //if (lPool == NULL and hPool == NULL) create new pool where low = ip = high
465         if (lPool == null && hPool == null) {
466             allocationPools.add(new NeutronSubnet_IPAllocationPool(ipAddress,ipAddress));
467         }
468         //if (lPool == NULL and hPool != NULL) change low address of hPool to ipAddr
469         if (lPool == null && hPool != null) {
470             hPool.setPoolStart(ipAddress);
471         }
472         //if (lPool != NULL and hPool == NULL) change high address of lPool to ipAddr
473         if (lPool != null && hPool == null) {
474             lPool.setPoolEnd(ipAddress);
475         }
476         //if (lPool != NULL and hPool != NULL) remove lPool and hPool and create new pool
477         //        where low address = lPool.low address and high address = hPool.high Address
478         if (lPool != null && hPool != null) {
479             allocationPools.remove(lPool);
480             allocationPools.remove(hPool);
481             allocationPools.add(new NeutronSubnet_IPAllocationPool(
482                     lPool.getPoolStart(), hPool.getPoolEnd()));
483         }
484     }
485
486     public void setGatewayIPAllocated() {
487         gatewayIPAssigned = true;
488     }
489
490     public void resetGatewayIPAllocated() {
491         gatewayIPAssigned = false;
492     }
493
494     public Boolean getGatewayIPAllocated() {
495         return gatewayIPAssigned;
496     }
497
498     @Override
499     public String toString() {
500         return "NeutronSubnet [subnetUUID=" + subnetUUID + ", networkUUID=" + networkUUID + ", name=" + name
501                 + ", ipVersion=" + ipVersion + ", cidr=" + cidr + ", gatewayIP=" + gatewayIP + ", dnsNameservers="
502                 + dnsNameservers + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes
503                 + ", enableDHCP=" + enableDHCP + ", tenantID=" + tenantID + ", myPorts=" + myPorts
504                 + ", gatewayIPAssigned=" + gatewayIPAssigned + ", ipv6AddressMode=" + ipV6AddressMode
505                 + ", ipv6RaMode=" + ipV6RaMode + "]";
506     }
507 }