checkstyle: enable JavadocTagContinuationIndentation
[neutron.git] / neutron-spi / src / main / java / org / opendaylight / neutron / spi / NeutronSubnet.java
1 /*
2  * Copyright (c) 2013, 2015 IBM Corporation and others.  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.neutron.spi;
10
11 import java.io.Serializable;
12 import java.math.BigInteger;
13 import java.net.Inet6Address;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.ArrayList;
17 import java.util.List;
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 import org.apache.commons.net.util.SubnetUtils;
23 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 @XmlRootElement
28 @XmlAccessorType(XmlAccessType.NONE)
29 public final class NeutronSubnet extends NeutronBaseAttributes<NeutronSubnet> implements Serializable {
30     private static final Logger LOGGER = LoggerFactory.getLogger(NeutronCRUDInterfaces.class);
31
32     private static final long serialVersionUID = 1L;
33     private static final int IPV4_VERSION = 4;
34     private static final int IPV6_VERSION = 6;
35     private static final int IPV6_LENGTH = 128;
36     private static final int IPV6_LENGTH_BYTES = 8;
37     private static final long IPV6_LSB_MASK = 0x000000FF;
38     private static final int IPV6_BYTE_OFFSET = 7;
39
40     // See OpenStack Network API v2.0 Reference for description of
41     // annotated attributes
42
43     @XmlElement(name = "network_id")
44     String networkUUID;
45
46     @XmlElement(defaultValue = "4", name = "ip_version")
47     Integer ipVersion;
48
49     @XmlElement(name = "cidr")
50     String cidr;
51
52     @XmlElement(name = "gateway_ip")
53     String gatewayIP;
54
55     @XmlElement(name = "dns_nameservers")
56     List<String> dnsNameservers;
57
58     @XmlElement(name = "allocation_pools")
59     List<NeutronSubnetIPAllocationPool> allocationPools;
60
61     @XmlElement(name = "host_routes")
62     List<NeutronRoute> hostRoutes;
63
64     @XmlElement(defaultValue = "true", name = "enable_dhcp")
65     Boolean enableDHCP;
66
67     @XmlElement(name = "ipv6_address_mode", nillable = true)
68     String ipV6AddressMode;
69
70     @XmlElement(name = "ipv6_ra_mode", nillable = true)
71     String ipV6RaMode;
72
73     public String getNetworkUUID() {
74         return networkUUID;
75     }
76
77     public void setNetworkUUID(String networkUUID) {
78         this.networkUUID = networkUUID;
79     }
80
81     public Integer getIpVersion() {
82         return ipVersion;
83     }
84
85     public void setIpVersion(Integer ipVersion) {
86         this.ipVersion = ipVersion;
87     }
88
89     public String getCidr() {
90         return cidr;
91     }
92
93     public void setCidr(String cidr) {
94         this.cidr = cidr;
95     }
96
97     public String getGatewayIP() {
98         return gatewayIP;
99     }
100
101     public void setGatewayIP(String gatewayIP) {
102         this.gatewayIP = gatewayIP;
103     }
104
105     public List<String> getDnsNameservers() {
106         return dnsNameservers;
107     }
108
109     public void setDnsNameservers(List<String> dnsNameservers) {
110         this.dnsNameservers = dnsNameservers;
111     }
112
113     public List<NeutronSubnetIPAllocationPool> getAllocationPools() {
114         return allocationPools;
115     }
116
117     public void setAllocationPools(List<NeutronSubnetIPAllocationPool> allocationPools) {
118         this.allocationPools = allocationPools;
119     }
120
121     public List<NeutronRoute> getHostRoutes() {
122         return hostRoutes;
123     }
124
125     public void setHostRoutes(List<NeutronRoute> hostRoutes) {
126         this.hostRoutes = hostRoutes;
127     }
128
129     public boolean isEnableDHCP() {
130         if (enableDHCP == null) {
131             return true;
132         }
133         return enableDHCP;
134     }
135
136     public Boolean getEnableDHCP() {
137         return enableDHCP;
138     }
139
140     public void setEnableDHCP(Boolean newValue) {
141         enableDHCP = newValue;
142     }
143
144     public String getIpV6AddressMode() {
145         return ipV6AddressMode;
146     }
147
148     public void setIpV6AddressMode(String ipV6AddressMode) {
149         this.ipV6AddressMode = ipV6AddressMode;
150     }
151
152     public String getIpV6RaMode() {
153         return ipV6RaMode;
154     }
155
156     public void setIpV6RaMode(String ipV6RaMode) {
157         this.ipV6RaMode = ipV6RaMode;
158     }
159
160     /**
161      * This method copies selected fields from the object and returns them
162      * as a new object, suitable for marshaling.
163      *
164      * @param fields
165      *            List of attributes to be extracted
166      * @return an OpenStackSubnets object with only the selected fields
167      *             populated
168      */
169
170     public NeutronSubnet extractFields(List<String> fields) {
171         NeutronSubnet ans = new NeutronSubnet();
172         for (String s : fields) {
173             extractField(s, ans);
174             if (s.equals("network_id")) {
175                 ans.setNetworkUUID(this.getNetworkUUID());
176             }
177             if (s.equals("ip_version")) {
178                 ans.setIpVersion(this.getIpVersion());
179             }
180             if (s.equals("cidr")) {
181                 ans.setCidr(this.getCidr());
182             }
183             if (s.equals("gateway_ip")) {
184                 ans.setGatewayIP(this.getGatewayIP());
185             }
186             if (s.equals("dns_nameservers")) {
187                 List<String> nsList = new ArrayList<String>();
188                 nsList.addAll(this.getDnsNameservers());
189                 ans.setDnsNameservers(nsList);
190             }
191             if (s.equals("allocation_pools")) {
192                 List<NeutronSubnetIPAllocationPool> pools = new ArrayList<NeutronSubnetIPAllocationPool>();
193                 pools.addAll(this.getAllocationPools());
194                 ans.setAllocationPools(pools);
195             }
196             if (s.equals("host_routes")) {
197                 List<NeutronRoute> hostRoutes = new ArrayList<NeutronRoute>();
198                 hostRoutes.addAll(this.getHostRoutes());
199                 ans.setHostRoutes(hostRoutes);
200             }
201             if (s.equals("enable_dhcp")) {
202                 ans.setEnableDHCP(this.getEnableDHCP());
203             }
204             if (s.equals("ipv6_address_mode")) {
205                 ans.setIpV6AddressMode(this.getIpV6AddressMode());
206             }
207             if (s.equals("ipv6_ra_mode")) {
208                 ans.setIpV6RaMode(this.getIpV6RaMode());
209             }
210         }
211         return ans;
212     }
213
214     /* test to see if the cidr address used to define this subnet
215      * is a valid network address (an necessary condition when creating
216      * a new subnet)
217      */
218     public boolean isValidCIDR() {
219         // fix for Bug 2290 - need to wrap the existing test as
220         // IPv4 because SubnetUtils doesn't support IPv6
221         if (ipVersion == IPV4_VERSION) {
222             try {
223                 SubnetUtils util = new SubnetUtils(cidr);
224                 SubnetInfo info = util.getInfo();
225                 if (!info.getNetworkAddress().equals(info.getAddress())) {
226                     return false;
227                 }
228             } catch (IllegalArgumentException e) {
229                 LOGGER.warn("Failure in isValidCIDR()", e);
230                 return false;
231             }
232             return true;
233         }
234         if (ipVersion == IPV6_VERSION) {
235             // fix for Bug2290 - this is custom code because no classes
236             // with ODL-friendly licenses have been found
237             // extract address (in front of /) and length (after /)
238             String[] parts = cidr.split("/");
239             if (parts.length != 2) {
240                 return false;
241             }
242             try {
243                 int length = Integer.parseInt(parts[1]);
244                 //TODO?: limit check on length
245                 // convert to byte array
246                 byte[] addrBytes = ((Inet6Address) InetAddress.getByName(parts[0])).getAddress();
247                 for (int index = length; index < IPV6_LENGTH; index++) {
248                     if (((((int) addrBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK)
249                             & (1 << (IPV6_BYTE_OFFSET - (index % IPV6_LENGTH_BYTES)))) != 0) {
250                         return (false);
251                     }
252                 }
253                 return (true);
254             } catch (UnknownHostException e) {
255                 LOGGER.warn("Failure in isValidCIDR()", e);
256                 return (false);
257             }
258         }
259         return false;
260     }
261
262     /* test to see if the gateway IP specified overlaps with specified
263      * allocation pools (an error condition when creating a new subnet
264      * or assigning a gateway IP)
265      */
266     public boolean gatewayIP_Pool_overlap() {
267         for (NeutronSubnetIPAllocationPool pool : allocationPools) {
268             if (ipVersion == IPV4_VERSION && pool.contains(gatewayIP)) {
269                 return true;
270             }
271             if (ipVersion == IPV6_VERSION && pool.containsV6(gatewayIP)) {
272                 return true;
273             }
274         }
275         return false;
276     }
277
278     @Override
279     public void initDefaults() {
280         if (enableDHCP == null) {
281             enableDHCP = true;
282         }
283         if (ipVersion == null) {
284             ipVersion = IPV4_VERSION;
285         }
286         if (dnsNameservers == null) {
287             dnsNameservers = new ArrayList<String>();
288         }
289         if (hostRoutes == null) {
290             hostRoutes = new ArrayList<NeutronRoute>();
291         }
292         if (allocationPools == null) {
293             allocationPools = new ArrayList<NeutronSubnetIPAllocationPool>();
294             if (ipVersion == IPV4_VERSION) {
295                 try {
296                     SubnetUtils util = new SubnetUtils(cidr);
297                     SubnetInfo info = util.getInfo();
298                     if (gatewayIP == null || ("").equals(gatewayIP)) {
299                         gatewayIP = info.getLowAddress();
300                     }
301                     if (allocationPools.size() < 1) {
302                         NeutronSubnetIPAllocationPool source = new NeutronSubnetIPAllocationPool(info.getLowAddress(),
303                                 info.getHighAddress());
304                         allocationPools = source.splitPool(gatewayIP);
305                     }
306                 } catch (IllegalArgumentException e) {
307                     LOGGER.warn("Failure in initDefault()", e);
308                     return;
309                 }
310             }
311             if (ipVersion == IPV6_VERSION) {
312                 String[] parts = cidr.split("/");
313                 if (parts.length != 2) {
314                     return;
315                 }
316
317                 int length = Integer.parseInt(parts[1]);
318                 BigInteger lowAddressBi = NeutronSubnetIPAllocationPool.convertV6(parts[0]);
319                 String lowAddress = NeutronSubnetIPAllocationPool.bigIntegerToIP(lowAddressBi.add(BigInteger.ONE));
320                 BigInteger mask = BigInteger.ONE.shiftLeft(length).subtract(BigInteger.ONE);
321                 String highAddress = NeutronSubnetIPAllocationPool
322                         .bigIntegerToIP(lowAddressBi.add(mask).subtract(BigInteger.ONE));
323                 if (gatewayIP == null || ("").equals(gatewayIP)) {
324                     gatewayIP = lowAddress;
325                 }
326                 if (allocationPools.size() < 1) {
327                     NeutronSubnetIPAllocationPool source = new NeutronSubnetIPAllocationPool(lowAddress,
328                             highAddress);
329                     allocationPools = source.splitPoolV6(gatewayIP);
330                 }
331             }
332         }
333     }
334
335     /* this method tests to see if the supplied IPv4 address
336      * is valid for this subnet or not
337      */
338     public boolean isValidIP(String ipAddress) {
339         if (ipVersion == IPV4_VERSION) {
340             try {
341                 SubnetUtils util = new SubnetUtils(cidr);
342                 SubnetInfo info = util.getInfo();
343                 return info.isInRange(ipAddress);
344             } catch (IllegalArgumentException e) {
345                 LOGGER.warn("Failure in isValidIP()", e);
346                 return false;
347             }
348         }
349
350         if (ipVersion == IPV6_VERSION) {
351             String[] parts = cidr.split("/");
352             try {
353                 int length = Integer.parseInt(parts[1]);
354                 byte[] cidrBytes = ((Inet6Address) InetAddress.getByName(parts[0])).getAddress();
355                 byte[] ipBytes = ((Inet6Address) InetAddress.getByName(ipAddress)).getAddress();
356                 for (int index = 0; index < length; index++) {
357                     if (((((int) cidrBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK) & (1 << (IPV6_BYTE_OFFSET
358                             - (index % IPV6_LENGTH_BYTES)))) != (
359                                 (((int) ipBytes[index / IPV6_LENGTH_BYTES]) & IPV6_LSB_MASK)
360                                 & (1 << (IPV6_BYTE_OFFSET - (index % IPV6_LENGTH_BYTES))))) {
361                         return (false);
362                     }
363                 }
364                 return (true);
365             } catch (UnknownHostException e) {
366                 LOGGER.warn("Failure in isValidIP()", e);
367                 return (false);
368             }
369         }
370         return false;
371     }
372
373     /* method to get the lowest available address of the subnet.
374      * go through all the allocation pools and keep the lowest of their
375      * low addresses.
376      */
377     public String getLowAddr() {
378         String ans = null;
379         for (NeutronSubnetIPAllocationPool pool : allocationPools) {
380             if (ans == null) {
381                 ans = pool.getPoolStart();
382             } else {
383                 if (ipVersion == IPV4_VERSION && NeutronSubnetIPAllocationPool
384                         .convert(pool.getPoolStart()) < NeutronSubnetIPAllocationPool.convert(ans)) {
385                     ans = pool.getPoolStart();
386                 }
387                 if (ipVersion == IPV6_VERSION && NeutronSubnetIPAllocationPool.convertV6(pool.getPoolStart())
388                         .compareTo(NeutronSubnetIPAllocationPool.convertV6(ans)) < 0) {
389                     ans = pool.getPoolStart();
390                 }
391             }
392         }
393         return ans;
394     }
395
396     @Override
397     public String toString() {
398         return "NeutronSubnet [subnetUUID=" + uuid + ", networkUUID=" + networkUUID + ", name=" + name + ", ipVersion="
399                 + ipVersion + ", cidr=" + cidr + ", gatewayIP=" + gatewayIP + ", dnsNameservers=" + dnsNameservers
400                 + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes + ", enableDHCP=" + enableDHCP
401                 + ", tenantID=" + tenantID + ", ipv6AddressMode=" + ipV6AddressMode + ", ipv6RaMode=" + ipV6RaMode
402                 + "]";
403     }
404 }