caf8a13e63d20e9bfbe637483d6aaca82ff1d732
[controller.git] / opendaylight / containermanager / api / src / main / java / org / opendaylight / controller / containermanager / ContainerConfig.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.containermanager;
11
12 import java.io.Serializable;
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
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.opendaylight.controller.sal.core.ContainerFlow;
24 import org.opendaylight.controller.sal.core.NodeConnector;
25 import org.opendaylight.controller.sal.match.Match;
26 import org.opendaylight.controller.sal.utils.GlobalConstants;
27 import org.opendaylight.controller.sal.utils.Status;
28 import org.opendaylight.controller.sal.utils.StatusCode;
29
30 /**
31  * Container Configuration Java Object for Container Manager Represents a container
32  * configuration information for Container Manager.
33  *
34  * Objects of this class are also serialized to and deserialized from binary
35  * files through java serialization API when saving to/reading from Container
36  * Manager startup configuration file.
37  */
38 @XmlRootElement(name = "container-config")
39 @XmlAccessorType(XmlAccessType.NONE)
40 public class ContainerConfig implements Serializable {
41     private static final long serialVersionUID = 2L;
42     private static final String regexName = "^\\w+$";
43
44     @XmlElement
45     private String container;
46
47     @XmlElement
48     private String staticVlan;
49
50     @XmlElement(name = "nodeConnectors")
51     private List<String> ports;
52
53     @XmlElement(name = "flowSpecs")
54     private List<ContainerFlowConfig> containerFlows;
55
56     public String getContainer() {
57         return container;
58     }
59
60     public void setContainer(String container) {
61         this.container = container;
62     }
63
64     public String getStaticVlan() {
65         return staticVlan;
66     }
67
68     public void setStaticVlan(String staticVlan) {
69         this.staticVlan = staticVlan;
70     }
71
72     public List<ContainerFlowConfig> getContainerFlows() {
73         return containerFlows;
74     }
75
76     public void setContainerFlows(List<ContainerFlowConfig> containerFlows) {
77         this.containerFlows = containerFlows;
78     }
79
80     public static long getSerialversionuid() {
81         return serialVersionUID;
82     }
83
84     public static String getRegexname() {
85         return regexName;
86     }
87
88     public void setPorts(List<String> ports) {
89         this.ports = ports;
90     }
91
92     /**
93      * Default constructor needed by Gson.
94      *
95      * @return a Default ContainerConfig
96      */
97     public ContainerConfig() {
98         this.container = null;
99         this.staticVlan = null;
100         this.ports = new ArrayList<String>(0);
101         this.containerFlows = new ArrayList<ContainerFlowConfig>(0);
102     }
103
104     /**
105      * Constructor for the ContainerConfig.
106      *
107      * @param container
108      *            Name of the container in this configuration
109      * @param vlan
110      *            vlan assigned to this container
111      * @param nodeName
112      *            the name of the node assigned to the container from this
113      *            configuration
114      * @param ports
115      *            the list of NodeConnectors on the Node belonging to the container
116      * @return the constructed object
117      */
118     public ContainerConfig(String container, String vlan, List<String> portList, List<ContainerFlowConfig> containerFlows) {
119         this.container = container;
120         this.staticVlan = vlan;
121         this.ports = (portList == null) ? new ArrayList<String>(0) : new ArrayList<String>(portList);
122         this.containerFlows = (containerFlows == null) ? new ArrayList<ContainerFlowConfig>(0)
123                 : new ArrayList<ContainerFlowConfig>(containerFlows);
124     }
125
126     public ContainerConfig(ContainerConfig config) {
127         this.container = config.container;
128         this.staticVlan = config.staticVlan;
129         this.ports = (config.ports == null) ? new ArrayList<String>(0) : new ArrayList<String>(config.ports);
130         this.containerFlows = (config.containerFlows == null) ? new ArrayList<ContainerFlowConfig>(0)
131                 : new ArrayList<ContainerFlowConfig>(config.containerFlows);
132     }
133
134     /**
135      * Returns the container name.
136      *
137      * @return the container name
138      */
139     public String getContainerName() {
140         return container;
141     }
142
143     /**
144      * Returns the Vlan tag.
145      *
146      * @return the Vlan Tag configured for this container configuration
147      */
148     public String getVlanTag() {
149         return staticVlan;
150     }
151
152     /**
153      * Returns the configured ports.
154      *
155      * @return the string with the list of ports associated to the container on this
156      *         configuration
157      */
158     public List<String> getPorts() {
159         return new ArrayList<String>(ports);
160     }
161
162     /**
163      * Returns the list of container flows configured for this container
164      *
165      * @return
166      */
167     public List<ContainerFlowConfig> getContainerFlowConfigs() {
168         return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList<ContainerFlowConfig>(0)
169                 : new ArrayList<ContainerFlowConfig>(containerFlows);
170     }
171
172     /**
173      * Matches container name against passed parameter.
174      *
175      * @param name
176      *            name of the container to be matched
177      * @return true if the passed argument correspond with the container name in the
178      *         configuration
179      */
180     public boolean matchName(String name) {
181         return this.container.equals(name);
182     }
183
184     /**
185      * Parse the port list in several NodeConnector descriptor.
186      *
187      * @return the list of NodeConnector corresponding to the ports configured
188      *         on this configuration
189      */
190     public List<NodeConnector> getPortList() {
191         List<NodeConnector> portList = new ArrayList<NodeConnector>();
192         if (ports != null && !ports.isEmpty()) {
193             for (String portString : ports) {
194                 portList.add(NodeConnector.fromString(portString));
195             }
196         }
197         return portList;
198     }
199
200     /**
201      * Checks if this is a valid container configuration
202      *
203      * @return true, if is valid container configuration, false otherwise
204      */
205     public Status validate() {
206         Status status = validateName();
207         if (status.isSuccess()) {
208             status = validateStaticVlan();
209             if (status.isSuccess()) {
210                 status = validatePorts();
211                 if (status.isSuccess()) {
212                     status = validateContainerFlows();
213                 }
214             }
215         }
216         return status;
217     }
218
219     /**
220      * Checks for valid name.
221      *
222      * @return true, if successful
223      */
224     private Status validateName() {
225         // No Container configuration allowed to container default
226         return ((container != null) && container.matches(regexName) && !container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) ?
227                 new Status(StatusCode.SUCCESS) : new Status(StatusCode.BADREQUEST, "Invalid container name");
228     }
229
230     /**
231      * Checks for valid ports.
232      *
233      * @return true, if successful
234      */
235     private Status validatePorts() {
236         return validateNodeConnectors(this.ports);
237     }
238
239     public static Status validateNodeConnectors(List<String> connectorList) {
240         if (connectorList != null && !connectorList.isEmpty()) {
241             for (String ncString : connectorList) {
242                 if (NodeConnector.fromString(ncString) == null) {
243                     return new Status(StatusCode.BADREQUEST, "Invalid node connector: " + ncString);
244                 }
245             }
246
247         }
248         return new Status(StatusCode.SUCCESS);
249     }
250
251     public static List<NodeConnector> nodeConnectorsFromString(List<String> nodeConnectorStrings) {
252         List<NodeConnector> list = new ArrayList<NodeConnector>(nodeConnectorStrings.size());
253         for (String str : nodeConnectorStrings) {
254             list.add(NodeConnector.fromString(str));
255         }
256         return list;
257     }
258
259     /**
260      * Checks for valid static vlan.
261      *
262      * @return true, if successful
263      */
264     private Status validateStaticVlan() {
265         if (staticVlan != null && !staticVlan.trim().isEmpty()) {
266             short vl = 0;
267             try {
268                 vl = Short.valueOf(staticVlan);
269             } catch (NumberFormatException e) {
270                 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
271             }
272             if ((vl < 1) || (vl > 4095)) {
273                 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
274             }
275         }
276         return new Status(StatusCode.SUCCESS);
277     }
278
279     private Status validateContainerFlows() {
280         if (containerFlows != null && !containerFlows.isEmpty()) {
281             for (ContainerFlowConfig conf : containerFlows) {
282                 Status status = conf.validate();
283                 if (!status.isSuccess()) {
284                     return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec: " + status.getDescription());
285                 }
286             }
287         }
288         return new Status(StatusCode.SUCCESS);
289     }
290
291     /**
292      * Returns Vlan value in short
293      *
294      * @return the Vlan tag
295      */
296     public short getStaticVlanValue() {
297         if ((staticVlan == null) || (staticVlan.trim().isEmpty())) {
298             return 0;
299         }
300         try {
301             return Short.valueOf(staticVlan);
302         } catch (NumberFormatException e) {
303             return 0;
304         }
305     }
306
307     public Status addNodeConnectors(List<String> ncList) {
308         // Syntax check
309         Status status = ContainerConfig.validateNodeConnectors(ncList);
310         if (!status.isSuccess()) {
311             return status;
312         }
313
314         // Add ports
315         ports.addAll(ncList);
316         return new Status(StatusCode.SUCCESS);
317     }
318
319     public Status removeNodeConnectors(List<String> ncList) {
320         // Syntax check
321         Status status = ContainerConfig.validateNodeConnectors(ncList);
322         if (!status.isSuccess()) {
323             return status;
324         }
325         // Presence check
326         if (ports.isEmpty()) {
327             return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
328                     + ncList);
329         }
330         List<String> extra = new ArrayList<String>(ncList);
331         extra.removeAll(ports);
332         if (!extra.isEmpty()) {
333             return new Status(StatusCode.CONFLICT, "The following node connectors are not part of this container: " + extra);
334         }
335         // Remove ports
336         ports.removeAll(ncList);
337         return new Status(StatusCode.SUCCESS);
338     }
339
340     public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
341         // Sanity Check
342         if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
343             return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
344         }
345         // Validity check
346         for (ContainerFlowConfig cFlowConf : cFlowConfigs) {
347             Status status = cFlowConf.validate();
348             if (!status.isSuccess()) {
349                 return new Status(StatusCode.BADREQUEST, String.format("Invalid Flow Spec configuration (%s): %s",
350                         cFlowConf.getName(), status.getDescription()));
351             }
352         }
353         // Name conflict check
354         List<String> existingNames = this.getContainerFlowConfigsNames();
355         List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
356
357         // Check for duplicates in the request
358         if (proposedNames.size() < cFlowConfigs.size()) {
359             return new Status(StatusCode.BADREQUEST,
360                     "Invalid Flow Spec configuration(s): duplicate name configs present");
361         }
362
363         // Check for overflow
364         if (delete) {
365             // Raw size check
366             if (proposedNames.size() > existingNames.size()) {
367                 return new Status(StatusCode.BADREQUEST,
368                         "Invalid request: requested to remove more flow spec configs than available ones");
369             }
370             // Presence check
371             for (ContainerFlowConfig config : cFlowConfigs) {
372                 if (!this.containerFlows.contains(config)) {
373                     return new Status(StatusCode.BADREQUEST, String.format(
374                             "Invalid request: requested to remove nonexistent flow spec config: %s",
375                             config.getName()));
376                 }
377             }
378         } else {
379             // Check for conflicting names with existing cFlows
380             List<String> conflicting = new ArrayList<String>(existingNames);
381             conflicting.retainAll(proposedNames);
382             if (!conflicting.isEmpty()) {
383                 return new Status(StatusCode.CONFLICT,
384                         "Invalid Flow Spec configuration: flow spec name(s) conflict with existing flow specs: "
385                                 + conflicting.toString());
386             }
387
388             /*
389              * Check for conflicting flow spec match (we only check for strict
390              * equality). Remove this in case (*) is reintroduced
391              */
392             if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
393                 Set<Match> existingMatches = new HashSet<Match>();
394                 for (ContainerFlowConfig existing : this.containerFlows) {
395                     existingMatches.addAll(existing.getMatches());
396                 }
397                 for (ContainerFlowConfig proposed : cFlowConfigs) {
398                     if (existingMatches.removeAll(proposed.getMatches())) {
399                         return new Status(StatusCode.CONFLICT, String.format(
400                                 "Invalid Flow Spec configuration: %s conflicts with existing flow spec",
401                                 proposed.getName()));
402                     }
403                 }
404             }
405         }
406
407
408         return new Status(StatusCode.SUCCESS);
409     }
410
411     public ContainerFlowConfig getContainerFlowConfig(String name) {
412         if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
413             for (ContainerFlowConfig conf : this.containerFlows) {
414                 if (conf.getName().equals(name)) {
415                     return new ContainerFlowConfig(conf);
416                 }
417             }
418         }
419         return null;
420     }
421
422     public List<String> getContainerFlowConfigsNames() {
423         return getContainerFlowConfigsNames(this.containerFlows);
424     }
425
426     /**
427      * Returns the list of unique names for the passed list of
428      * ContainerFlowConfig objects. the list will not contain duplicates even
429      * though the passed object list has ContainerFlowConfig objects with same
430      * names
431      *
432      * @param confList
433      *            the list of ContainerFlowConfig objects
434      * @return the list of correspondent unique container flow names. The return
435      *         list may differ from the passed list in size, if the latter
436      *         contains duplicates
437      */
438     private List<String> getContainerFlowConfigsNames(List<ContainerFlowConfig> confList) {
439         // Use set to check for duplicates later
440         Set<String> namesSet = new HashSet<String>();
441         if (confList != null) {
442             for (ContainerFlowConfig conf : confList) {
443                 namesSet.add(conf.getName());
444             }
445         }
446         return new ArrayList<String>(namesSet);
447     }
448
449     /**
450      * Add the proposed list of container flow configurations to this container
451      * configuration. A validation check on the operation is first run.
452      *
453      * @param containerFlowConfigs
454      *            the proposed list of container flow configuration objects to
455      *            add to this container configuration object
456      * @return the result of this request as Status object
457      */
458     public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
459         Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
460         if (!status.isSuccess()) {
461             return status;
462         }
463         if (this.containerFlows.addAll(containerFlowConfigs) == false) {
464             return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
465         }
466         return new Status(StatusCode.SUCCESS);
467     }
468
469     public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
470         Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
471         if (!status.isSuccess()) {
472             return status;
473         }
474         if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
475             return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
476         }
477         return new Status(StatusCode.SUCCESS);
478     }
479
480     public Status removeContainerFlows(Set<String> names) {
481         // Sanity check
482         if (names == null || names.isEmpty()) {
483             return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
484         }
485         // Validation check
486         List<String> present = this.getContainerFlowConfigsNames();
487         if (!present.containsAll(names)) {
488             List<String> notPresent = new ArrayList<String>(names);
489             notPresent.retainAll(present);
490             return new Status(StatusCode.BADREQUEST, "Following flow spec(s) are not present: " + notPresent);
491         }
492         // Remove
493         List<ContainerFlowConfig> toDelete = new ArrayList<ContainerFlowConfig>(names.size());
494         for (ContainerFlowConfig config : this.containerFlows) {
495             if (names.contains(config.getName())) {
496                 toDelete.add(config);
497             }
498         }
499         if (this.containerFlows.removeAll(toDelete) == false) {
500             return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
501         }
502         return new Status(StatusCode.SUCCESS);
503     }
504
505     public List<ContainerFlowConfig> getContainerFlowConfigs(Set<String> names) {
506         List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>(names.size());
507         for (String name : names) {
508             ContainerFlowConfig conf = this.getContainerFlowConfig(name);
509             if (conf != null) {
510                 list.add(new ContainerFlowConfig(conf));
511             }
512         }
513         return list;
514     }
515
516     @Override
517     public int hashCode() {
518         final int prime = 31;
519         int result = 1;
520         result = prime * result + ((containerFlows == null) ? 0 : containerFlows.hashCode());
521         result = prime * result + ((ports == null) ? 0 : ports.hashCode());
522         result = prime * result + ((container == null) ? 0 : container.hashCode());
523         result = prime * result + ((staticVlan == null) ? 0 : staticVlan.hashCode());
524         return result;
525     }
526
527     @Override
528     public boolean equals(Object obj) {
529         if (this == obj) {
530             return true;
531         }
532         if (obj == null) {
533             return false;
534         }
535         if (getClass() != obj.getClass()) {
536             return false;
537         }
538         ContainerConfig other = (ContainerConfig) obj;
539         if (containerFlows == null) {
540             if (other.containerFlows != null) {
541                 return false;
542             }
543         } else if (!containerFlows.equals(other.containerFlows)) {
544             return false;
545         }
546         if (ports == null) {
547             if (other.ports != null) {
548                 return false;
549             }
550         } else if (!ports.equals(other.ports)) {
551             return false;
552         }
553         if (container == null) {
554             if (other.container != null) {
555                 return false;
556             }
557         } else if (!container.equals(other.container)) {
558             return false;
559         }
560         if (staticVlan == null) {
561             if (other.staticVlan != null) {
562                 return false;
563             }
564         } else if (!staticVlan.equals(other.staticVlan)) {
565             return false;
566         }
567         return true;
568     }
569
570
571     /*
572      * (non-Javadoc)
573      *
574      * @see java.lang.Object#toString()
575      */
576     @Override
577     public String toString() {
578         String vlString = "";
579         if (staticVlan != null) {
580             vlString = staticVlan;
581         }
582         return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
583     }
584
585     /**
586      * Returns whether this Container configuration object has any ports specified
587      *
588      * @return true if any port is specified, false otherwise
589      */
590     public boolean hasNodeConnectors() {
591         return (ports != null && !ports.isEmpty());
592     }
593
594     /**
595      * Returns whether this Container configuration object has any flow specs specified
596      *
597      * @return true if any flow spec is specified, false otherwise
598      */
599     public boolean hasFlowSpecs() {
600         return (containerFlows != null && !containerFlows.isEmpty());
601     }
602
603     public List<ContainerFlow> getContainerFlowSpecs() {
604         List<ContainerFlow> list = new ArrayList<ContainerFlow>();
605         if (containerFlows != null && !containerFlows.isEmpty()) {
606             for (ContainerFlowConfig flowSpec : containerFlows) {
607                 for (Match match : flowSpec.getMatches()) {
608                     list.add(new ContainerFlow(match));
609                 }
610             }
611         }
612         return list;
613     }
614 }