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