Add password recovery procedure for default admin user
[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.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         /* Allow adding ports which are already present
315         if (!ports.isEmpty()) {
316             List<String> intersection = new ArrayList<String>(ports);
317             intersection.retainAll(ncList);
318             if (!intersection.isEmpty()) {
319                 return new Status(StatusCode.CONFLICT, "The following node connectors are already part of this container: "
320                         + intersection);
321             }
322         }
323         */
324
325         // Add ports
326         ports.addAll(ncList);
327         return new Status(StatusCode.SUCCESS);
328     }
329
330     public Status removeNodeConnectors(List<String> ncList) {
331         // Syntax check
332         Status status = ContainerConfig.validateNodeConnectors(ncList);
333         if (!status.isSuccess()) {
334             return status;
335         }
336         // Presence check
337         if (ports.isEmpty()) {
338             return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
339                     + ncList);
340         }
341         List<String> extra = new ArrayList<String>(ncList);
342         extra.removeAll(ports);
343         if (!extra.isEmpty()) {
344             return new Status(StatusCode.CONFLICT, "The following node connectors are not part of this container: " + extra);
345         }
346         // Remove ports
347         ports.removeAll(ncList);
348         return new Status(StatusCode.SUCCESS);
349     }
350
351     public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
352         // Sanity Check
353         if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
354             return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
355         }
356         // Validity check
357         for (ContainerFlowConfig cFlowConf : cFlowConfigs) {
358             Status status = cFlowConf.validate();
359             if (!status.isSuccess()) {
360                 return new Status(StatusCode.BADREQUEST, String.format("Invalid Flow Spec configuration (%s): %s",
361                         cFlowConf.getName(), status.getDescription()));
362             }
363         }
364         // Name conflict check
365         List<String> existingNames = this.getContainerFlowConfigsNames();
366         List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
367
368         // Check for duplicates in the request
369         if (proposedNames.size() < cFlowConfigs.size()) {
370             return new Status(StatusCode.BADREQUEST,
371                     "Invalid Flow Spec configuration(s): duplicate name configs present");
372         }
373
374         // Check for overflow
375         if (delete) {
376             // Raw size check
377             if (proposedNames.size() > existingNames.size()) {
378                 return new Status(StatusCode.BADREQUEST,
379                         "Invalid request: requested to remove more flow spec configs than available ones");
380             }
381             // Presence check
382             for (ContainerFlowConfig config : cFlowConfigs) {
383                 if (!this.containerFlows.contains(config)) {
384                     return new Status(StatusCode.BADREQUEST, String.format(
385                             "Invalid request: requested to remove nonexistent flow spec config: %s",
386                             config.getName()));
387                 }
388             }
389         }
390         /*
391          * Revisit the following flow-spec confict validation later based on more testing.
392          */
393          /*
394         if (!delete) {
395             // Check for overlapping container flows in the request
396             int size = cFlowConfigs.size();
397             for (int i = 0; i < size; i++) {
398                 ContainerFlowConfig first = cFlowConfigs.get(i);
399                 for (int j = i + 1; j < size; j++) {
400                     ContainerFlowConfig second = cFlowConfigs.get(j);
401                     if (first.overlap(second)) {
402                         return new Status(StatusCode.BADREQUEST, String.format(
403                                 "Invalid Request: the proposed flow specs overlap: %s <-> %s", first.getName(),
404                                 second.getName()));
405                     }
406                 }
407             }
408             // Check if any of the proposed container flows overlap with the
409             // existing ones
410             for (ContainerFlowConfig current : cFlowConfigs) {
411                 for (ContainerFlowConfig existing : this.containerFlows) {
412                     if (current.overlap(existing)) {
413                         return new Status(StatusCode.BADREQUEST, String.format(
414                                 "Invalid Request: the proposed flow specs overlap: %s <-> %s", current.getName(),
415                                 existing.getName()));
416                     }
417                 }
418             }
419         }
420         */
421
422         return new Status(StatusCode.SUCCESS);
423     }
424
425     public ContainerFlowConfig getContainerFlowConfig(String name) {
426         if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
427             for (ContainerFlowConfig conf : this.containerFlows) {
428                 if (conf.getName().equals(name)) {
429                     return new ContainerFlowConfig(conf);
430                 }
431             }
432         }
433         return null;
434     }
435
436     public List<String> getContainerFlowConfigsNames() {
437         return getContainerFlowConfigsNames(this.containerFlows);
438     }
439
440     /**
441      * Returns the list of unique names for the passed list of
442      * ContainerFlowConfig objects. the list will not contain duplicates even
443      * though the passed object list has ContainerFlowConfig objects with same
444      * names
445      *
446      * @param confList
447      *            the list of ContainerFlowConfig objects
448      * @return the list of correspondent unique container flow names. The return
449      *         list may differ from the passed list in size, if the latter
450      *         contains duplicates
451      */
452     private List<String> getContainerFlowConfigsNames(List<ContainerFlowConfig> confList) {
453         // Use set to check for duplicates later
454         Set<String> namesSet = new HashSet<String>();
455         if (confList != null) {
456             for (ContainerFlowConfig conf : confList) {
457                 namesSet.add(conf.getName());
458             }
459         }
460         return new ArrayList<String>(namesSet);
461     }
462
463     /**
464      * Add the proposed list of container flow configurations to this container
465      * configuration. A validation check on the operation is first run.
466      *
467      * @param containerFlowConfigs
468      *            the proposed list of container flow configuration objects to
469      *            add to this container configuration object
470      * @return the result of this request as Status object
471      */
472     public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
473         Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
474         if (!status.isSuccess()) {
475             return status;
476         }
477         if (this.containerFlows.addAll(containerFlowConfigs) == false) {
478             return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
479         }
480         return new Status(StatusCode.SUCCESS);
481     }
482
483     public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
484         Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
485         if (!status.isSuccess()) {
486             return status;
487         }
488         if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
489             return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
490         }
491         return new Status(StatusCode.SUCCESS);
492     }
493
494     public Status removeContainerFlows(Set<String> names) {
495         // Sanity check
496         if (names == null || names.isEmpty()) {
497             return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
498         }
499         // Validation check
500         List<String> present = this.getContainerFlowConfigsNames();
501         if (!present.containsAll(names)) {
502             List<String> notPresent = new ArrayList<String>(names);
503             notPresent.retainAll(present);
504             return new Status(StatusCode.BADREQUEST, "Following flow spec(s) are not present: " + notPresent);
505         }
506         // Remove
507         List<ContainerFlowConfig> toDelete = new ArrayList<ContainerFlowConfig>(names.size());
508         for (ContainerFlowConfig config : this.containerFlows) {
509             if (names.contains(config.getName())) {
510                 toDelete.add(config);
511             }
512         }
513         if (this.containerFlows.removeAll(toDelete) == false) {
514             return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
515         }
516         return new Status(StatusCode.SUCCESS);
517     }
518
519     public List<ContainerFlowConfig> getContainerFlowConfigs(Set<String> names) {
520         List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>(names.size());
521         for (String name : names) {
522             ContainerFlowConfig conf = this.getContainerFlowConfig(name);
523             if (conf != null) {
524                 list.add(new ContainerFlowConfig(conf));
525             }
526         }
527         return list;
528     }
529
530     @Override
531     public int hashCode() {
532         final int prime = 31;
533         int result = 1;
534         result = prime * result + ((containerFlows == null) ? 0 : containerFlows.hashCode());
535         result = prime * result + ((ports == null) ? 0 : ports.hashCode());
536         result = prime * result + ((container == null) ? 0 : container.hashCode());
537         result = prime * result + ((staticVlan == null) ? 0 : staticVlan.hashCode());
538         return result;
539     }
540
541     @Override
542     public boolean equals(Object obj) {
543         if (this == obj) {
544             return true;
545         }
546         if (obj == null) {
547             return false;
548         }
549         if (getClass() != obj.getClass()) {
550             return false;
551         }
552         ContainerConfig other = (ContainerConfig) obj;
553         if (containerFlows == null) {
554             if (other.containerFlows != null) {
555                 return false;
556             }
557         } else if (!containerFlows.equals(other.containerFlows)) {
558             return false;
559         }
560         if (ports == null) {
561             if (other.ports != null) {
562                 return false;
563             }
564         } else if (!ports.equals(other.ports)) {
565             return false;
566         }
567         if (container == null) {
568             if (other.container != null) {
569                 return false;
570             }
571         } else if (!container.equals(other.container)) {
572             return false;
573         }
574         if (staticVlan == null) {
575             if (other.staticVlan != null) {
576                 return false;
577             }
578         } else if (!staticVlan.equals(other.staticVlan)) {
579             return false;
580         }
581         return true;
582     }
583
584
585     /*
586      * (non-Javadoc)
587      *
588      * @see java.lang.Object#toString()
589      */
590     @Override
591     public String toString() {
592         String vlString = "";
593         if (staticVlan != null) {
594             vlString = staticVlan;
595         }
596         return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
597     }
598
599     /**
600      * Returns whether this Container configuration object has any ports specified
601      *
602      * @return true if any port is specified, false otherwise
603      */
604     public boolean hasNodeConnectors() {
605         return (ports != null && !ports.isEmpty());
606     }
607
608     /**
609      * Returns whether this Container configuration object has any flow specs specified
610      *
611      * @return true if any flow spec is specified, false otherwise
612      */
613     public boolean hasFlowSpecs() {
614         return (containerFlows != null && !containerFlows.isEmpty());
615     }
616
617     public List<ContainerFlow> getContainerFlowSpecs() {
618         List<ContainerFlow> list = new ArrayList<ContainerFlow>();
619         if (containerFlows != null && !containerFlows.isEmpty()) {
620             for (ContainerFlowConfig flowSpec : containerFlows) {
621                 for (Match match : flowSpec.getMatches()) {
622                     list.add(new ContainerFlow(match));
623                 }
624             }
625         }
626         return list;
627     }
628 }