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