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.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",
391 * Revisit the following flow-spec confict validation later based on more testing.
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(),
408 // Check if any of the proposed container flows overlap with the
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()));
422 return new Status(StatusCode.SUCCESS);
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);
436 public List<String> getContainerFlowConfigsNames() {
437 return getContainerFlowConfigsNames(this.containerFlows);
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
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
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());
460 return new ArrayList<String>(namesSet);
464 * Add the proposed list of container flow configurations to this container
465 * configuration. A validation check on the operation is first run.
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
472 public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
473 Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
474 if (!status.isSuccess()) {
477 if (this.containerFlows.addAll(containerFlowConfigs) == false) {
478 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
480 return new Status(StatusCode.SUCCESS);
483 public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
484 Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
485 if (!status.isSuccess()) {
488 if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
489 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
491 return new Status(StatusCode.SUCCESS);
494 public Status removeContainerFlows(Set<String> names) {
496 if (names == null || names.isEmpty()) {
497 return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
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);
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);
513 if (this.containerFlows.removeAll(toDelete) == false) {
514 return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
516 return new Status(StatusCode.SUCCESS);
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);
524 list.add(new ContainerFlowConfig(conf));
531 public int hashCode() {
532 final int prime = 31;
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());
542 public boolean equals(Object obj) {
549 if (getClass() != obj.getClass()) {
552 ContainerConfig other = (ContainerConfig) obj;
553 if (containerFlows == null) {
554 if (other.containerFlows != null) {
557 } else if (!containerFlows.equals(other.containerFlows)) {
561 if (other.ports != null) {
564 } else if (!ports.equals(other.ports)) {
567 if (container == null) {
568 if (other.container != null) {
571 } else if (!container.equals(other.container)) {
574 if (staticVlan == null) {
575 if (other.staticVlan != null) {
578 } else if (!staticVlan.equals(other.staticVlan)) {
588 * @see java.lang.Object#toString()
591 public String toString() {
592 String vlString = "";
593 if (staticVlan != null) {
594 vlString = staticVlan;
596 return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
600 * Returns whether this Container configuration object has any ports specified
602 * @return true if any port is specified, false otherwise
604 public boolean hasNodeConnectors() {
605 return (ports != null && !ports.isEmpty());
609 * Returns whether this Container configuration object has any flow specs specified
611 * @return true if any flow spec is specified, false otherwise
613 public boolean hasFlowSpecs() {
614 return (containerFlows != null && !containerFlows.isEmpty());
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));