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