3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
10 package org.opendaylight.controller.containermanager;
12 import java.io.Serializable;
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
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;
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;
31 * Container Configuration Java Object for Container Manager Represents a container
32 * configuration information for Container Manager.
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.
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+$";
45 private String container;
48 private String staticVlan;
50 @XmlElement(name = "nodeConnectors")
51 private List<String> ports;
53 @XmlElement(name = "flowSpecs")
54 private List<ContainerFlowConfig> containerFlows;
56 public String getContainer() {
60 public void setContainer(String container) {
61 this.container = container;
64 public String getStaticVlan() {
68 public void setStaticVlan(String staticVlan) {
69 this.staticVlan = staticVlan;
72 public List<ContainerFlowConfig> getContainerFlows() {
73 return containerFlows;
76 public void setContainerFlows(List<ContainerFlowConfig> containerFlows) {
77 this.containerFlows = containerFlows;
80 public static long getSerialversionuid() {
81 return serialVersionUID;
84 public static String getRegexname() {
88 public void setPorts(List<String> ports) {
93 * Default constructor needed by Gson.
95 * @return a Default ContainerConfig
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);
105 * Constructor for the ContainerConfig.
108 * Name of the container in this configuration
110 * vlan assigned to this container
112 * the name of the node assigned to the container from this
115 * the list of NodeConnectors on the Node belonging to the container
116 * @return the constructed object
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);
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);
135 * Returns the container name.
137 * @return the container name
139 public String getContainerName() {
144 * Returns the Vlan tag.
146 * @return the Vlan Tag configured for this container configuration
148 public String getVlanTag() {
153 * Returns the configured ports.
155 * @return the string with the list of ports associated to the container on this
158 public List<String> getPorts() {
159 return new ArrayList<String>(ports);
163 * Returns the list of container flows configured for this container
167 public List<ContainerFlowConfig> getContainerFlowConfigs() {
168 return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList<ContainerFlowConfig>(0)
169 : new ArrayList<ContainerFlowConfig>(containerFlows);
173 * Matches container name against passed parameter.
176 * name of the container to be matched
177 * @return true if the passed argument correspond with the container name in the
180 public boolean matchName(String name) {
181 return this.container.equals(name);
185 * Parse the port list in several NodeConnector descriptor.
187 * @return the list of NodeConnector corresponding to the ports configured
188 * on this configuration
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));
201 * Checks if this is a valid container configuration
203 * @return true, if is valid container configuration, false otherwise
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();
220 * Checks for valid name.
222 * @return true, if successful
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");
231 * Checks for valid ports.
233 * @return true, if successful
235 private Status validatePorts() {
236 return validateNodeConnectors(this.ports);
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);
248 return new Status(StatusCode.SUCCESS);
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));
260 * Checks for valid static vlan.
262 * @return true, if successful
264 private Status validateStaticVlan() {
265 if (staticVlan != null && !staticVlan.trim().isEmpty()) {
268 vl = Short.valueOf(staticVlan);
269 } catch (NumberFormatException e) {
270 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
272 if ((vl < 1) || (vl > 4095)) {
273 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
276 return new Status(StatusCode.SUCCESS);
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());
288 return new Status(StatusCode.SUCCESS);
292 * Returns Vlan value in short
294 * @return the Vlan tag
296 public short getStaticVlanValue() {
297 if ((staticVlan == null) || (staticVlan.trim().isEmpty())) {
301 return Short.valueOf(staticVlan);
302 } catch (NumberFormatException e) {
307 public Status addNodeConnectors(List<String> ncList) {
309 Status status = ContainerConfig.validateNodeConnectors(ncList);
310 if (!status.isSuccess()) {
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: "
326 ports.addAll(ncList);
327 return new Status(StatusCode.SUCCESS);
330 public Status removeNodeConnectors(List<String> ncList) {
332 Status status = ContainerConfig.validateNodeConnectors(ncList);
333 if (!status.isSuccess()) {
337 if (ports.isEmpty()) {
338 return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
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);
347 ports.removeAll(ncList);
348 return new Status(StatusCode.SUCCESS);
351 public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
353 if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
354 return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
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()));
364 // Name conflict check
365 List<String> existingNames = this.getContainerFlowConfigsNames();
366 List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
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");
374 // Check for overflow
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");
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",
390 // Check for conflicting names with existing cFlows
391 List<String> conflicting = new ArrayList<String>(existingNames);
392 conflicting.retainAll(proposedNames);
393 if (!conflicting.isEmpty()) {
394 return new Status(StatusCode.CONFLICT,
395 "Invalid Flow Spec configuration: flow spec name(s) conflict with existing flow specs: "
396 + conflicting.toString());
400 * Check for conflicting flow spec match (we only check for strict
401 * equality). Remove this in case (*) is reintroduced
403 if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
404 Set<Match> existingMatches = new HashSet<Match>();
405 for (ContainerFlowConfig existing : this.containerFlows) {
406 existingMatches.addAll(existing.getMatches());
408 for (ContainerFlowConfig proposed : cFlowConfigs) {
409 if (existingMatches.removeAll(proposed.getMatches())) {
410 return new Status(StatusCode.CONFLICT, String.format(
411 "Invalid Flow Spec configuration: %s conflicts with existing flow spec",
412 proposed.getName()));
419 * Revisit the following flow-spec confict validation later based on more testing.
422 // Check for overlapping container flows in the request
423 int size = cFlowConfigs.size();
424 for (int i = 0; i < size; i++) {
425 ContainerFlowConfig first = cFlowConfigs.get(i);
426 for (int j = i + 1; j < size; j++) {
427 ContainerFlowConfig second = cFlowConfigs.get(j);
428 if (first.overlap(second)) {
429 return new Status(StatusCode.BADREQUEST, String.format(
430 "Invalid Request: the proposed flow specs overlap: %s <-> %s", first.getName(),
435 // Check if any of the proposed container flows overlap with the
437 for (ContainerFlowConfig current : cFlowConfigs) {
438 for (ContainerFlowConfig existing : this.containerFlows) {
439 if (current.overlap(existing)) {
440 return new Status(StatusCode.BADREQUEST, String.format(
441 "Invalid Request: the proposed flow specs overlap: %s <-> %s", current.getName(),
442 existing.getName()));
449 return new Status(StatusCode.SUCCESS);
452 public ContainerFlowConfig getContainerFlowConfig(String name) {
453 if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
454 for (ContainerFlowConfig conf : this.containerFlows) {
455 if (conf.getName().equals(name)) {
456 return new ContainerFlowConfig(conf);
463 public List<String> getContainerFlowConfigsNames() {
464 return getContainerFlowConfigsNames(this.containerFlows);
468 * Returns the list of unique names for the passed list of
469 * ContainerFlowConfig objects. the list will not contain duplicates even
470 * though the passed object list has ContainerFlowConfig objects with same
474 * the list of ContainerFlowConfig objects
475 * @return the list of correspondent unique container flow names. The return
476 * list may differ from the passed list in size, if the latter
477 * contains duplicates
479 private List<String> getContainerFlowConfigsNames(List<ContainerFlowConfig> confList) {
480 // Use set to check for duplicates later
481 Set<String> namesSet = new HashSet<String>();
482 if (confList != null) {
483 for (ContainerFlowConfig conf : confList) {
484 namesSet.add(conf.getName());
487 return new ArrayList<String>(namesSet);
491 * Add the proposed list of container flow configurations to this container
492 * configuration. A validation check on the operation is first run.
494 * @param containerFlowConfigs
495 * the proposed list of container flow configuration objects to
496 * add to this container configuration object
497 * @return the result of this request as Status object
499 public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
500 Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
501 if (!status.isSuccess()) {
504 if (this.containerFlows.addAll(containerFlowConfigs) == false) {
505 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
507 return new Status(StatusCode.SUCCESS);
510 public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
511 Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
512 if (!status.isSuccess()) {
515 if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
516 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
518 return new Status(StatusCode.SUCCESS);
521 public Status removeContainerFlows(Set<String> names) {
523 if (names == null || names.isEmpty()) {
524 return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
527 List<String> present = this.getContainerFlowConfigsNames();
528 if (!present.containsAll(names)) {
529 List<String> notPresent = new ArrayList<String>(names);
530 notPresent.retainAll(present);
531 return new Status(StatusCode.BADREQUEST, "Following flow spec(s) are not present: " + notPresent);
534 List<ContainerFlowConfig> toDelete = new ArrayList<ContainerFlowConfig>(names.size());
535 for (ContainerFlowConfig config : this.containerFlows) {
536 if (names.contains(config.getName())) {
537 toDelete.add(config);
540 if (this.containerFlows.removeAll(toDelete) == false) {
541 return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
543 return new Status(StatusCode.SUCCESS);
546 public List<ContainerFlowConfig> getContainerFlowConfigs(Set<String> names) {
547 List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>(names.size());
548 for (String name : names) {
549 ContainerFlowConfig conf = this.getContainerFlowConfig(name);
551 list.add(new ContainerFlowConfig(conf));
558 public int hashCode() {
559 final int prime = 31;
561 result = prime * result + ((containerFlows == null) ? 0 : containerFlows.hashCode());
562 result = prime * result + ((ports == null) ? 0 : ports.hashCode());
563 result = prime * result + ((container == null) ? 0 : container.hashCode());
564 result = prime * result + ((staticVlan == null) ? 0 : staticVlan.hashCode());
569 public boolean equals(Object obj) {
576 if (getClass() != obj.getClass()) {
579 ContainerConfig other = (ContainerConfig) obj;
580 if (containerFlows == null) {
581 if (other.containerFlows != null) {
584 } else if (!containerFlows.equals(other.containerFlows)) {
588 if (other.ports != null) {
591 } else if (!ports.equals(other.ports)) {
594 if (container == null) {
595 if (other.container != null) {
598 } else if (!container.equals(other.container)) {
601 if (staticVlan == null) {
602 if (other.staticVlan != null) {
605 } else if (!staticVlan.equals(other.staticVlan)) {
615 * @see java.lang.Object#toString()
618 public String toString() {
619 String vlString = "";
620 if (staticVlan != null) {
621 vlString = staticVlan;
623 return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
627 * Returns whether this Container configuration object has any ports specified
629 * @return true if any port is specified, false otherwise
631 public boolean hasNodeConnectors() {
632 return (ports != null && !ports.isEmpty());
636 * Returns whether this Container configuration object has any flow specs specified
638 * @return true if any flow spec is specified, false otherwise
640 public boolean hasFlowSpecs() {
641 return (containerFlows != null && !containerFlows.isEmpty());
644 public List<ContainerFlow> getContainerFlowSpecs() {
645 List<ContainerFlow> list = new ArrayList<ContainerFlow>();
646 if (containerFlows != null && !containerFlows.isEmpty()) {
647 for (ContainerFlowConfig flowSpec : containerFlows) {
648 for (Match match : flowSpec.getMatches()) {
649 list.add(new ContainerFlow(match));