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+$";
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";
49 private String container;
52 private String staticVlan;
54 @XmlElement(name = "nodeConnectors")
55 private List<String> ports;
57 @XmlElement(name = "flowSpecs")
58 private List<ContainerFlowConfig> containerFlows;
60 public String getContainer() {
64 public void setContainer(String container) {
65 this.container = container;
68 public String getStaticVlan() {
72 public void setStaticVlan(String staticVlan) {
73 this.staticVlan = staticVlan;
76 public List<ContainerFlowConfig> getContainerFlows() {
77 return containerFlows;
80 public void setContainerFlows(List<ContainerFlowConfig> containerFlows) {
81 this.containerFlows = containerFlows;
84 public static long getSerialversionuid() {
85 return serialVersionUID;
88 public static String getRegexname() {
92 public void setPorts(List<String> ports) {
97 * Default constructor needed by Gson.
99 * @return a Default ContainerConfig
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);
109 * Constructor for the ContainerConfig.
112 * Name of the container in this configuration
114 * vlan assigned to this container
116 * the name of the node assigned to the container from this
119 * the list of NodeConnectors on the Node belonging to the container
120 * @return the constructed object
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);
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);
139 * Returns the container name.
141 * @return the container name
143 public String getContainerName() {
148 * Returns the Vlan tag.
150 * @return the Vlan Tag configured for this container configuration
152 public String getVlanTag() {
157 * Returns the configured ports.
159 * @return the string with the list of ports associated to the container on this
162 public List<String> getPorts() {
163 return new ArrayList<String>(ports);
167 * Returns the list of container flows configured for this container
171 public List<ContainerFlowConfig> getContainerFlowConfigs() {
172 return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList<ContainerFlowConfig>(0)
173 : new ArrayList<ContainerFlowConfig>(containerFlows);
177 * Matches container name against passed parameter.
180 * name of the container to be matched
181 * @return true if the passed argument correspond with the container name in the
184 public boolean matchName(String name) {
185 return this.container.equals(name);
189 * Parse the port list in several NodeConnector descriptor.
191 * @return the list of NodeConnector corresponding to the ports configured
192 * on this configuration
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));
205 * Checks if this is a valid container configuration
207 * @return true, if is valid container configuration, false otherwise
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();
224 * Checks for valid name.
226 * @return true, if successful
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");
235 * Checks for valid ports.
237 * @return true, if successful
239 private Status validatePorts() {
240 return validateNodeConnectors(this.ports);
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);
252 return new Status(StatusCode.SUCCESS);
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));
264 * Checks for valid static vlan.
266 * @return true, if successful
268 private Status validateStaticVlan() {
269 if (staticVlan != null && !staticVlan.trim().isEmpty()) {
272 vl = Short.valueOf(staticVlan);
273 } catch (NumberFormatException e) {
274 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
276 if ((vl < 1) || (vl > 4095)) {
277 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
280 return new Status(StatusCode.SUCCESS);
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());
292 return new Status(StatusCode.SUCCESS);
296 * Returns Vlan value in short
298 * @return the Vlan tag
300 public short getStaticVlanValue() {
301 if ((staticVlan == null) || (staticVlan.trim().isEmpty())) {
305 return Short.valueOf(staticVlan);
306 } catch (NumberFormatException e) {
311 public Status addNodeConnectors(List<String> ncList) {
313 Status status = ContainerConfig.validateNodeConnectors(ncList);
314 if (!status.isSuccess()) {
319 ports.addAll(ncList);
320 return new Status(StatusCode.SUCCESS);
323 public Status removeNodeConnectors(List<String> ncList) {
325 Status status = ContainerConfig.validateNodeConnectors(ncList);
326 if (!status.isSuccess()) {
330 if (ports.isEmpty()) {
331 return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
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);
340 ports.removeAll(ncList);
341 return new Status(StatusCode.SUCCESS);
344 public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
346 if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
347 return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
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()));
357 // Name conflict check
358 List<String> existingNames = this.getContainerFlowConfigsNames();
359 List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
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");
367 // Check for overflow
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");
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",
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());
393 * Check for conflicting flow spec match (we only check for strict
394 * equality). Remove this in case (*) is reintroduced
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());
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()));
412 return new Status(StatusCode.SUCCESS);
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);
426 public List<String> getContainerFlowConfigsNames() {
427 return getContainerFlowConfigsNames(this.containerFlows);
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
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
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());
450 return new ArrayList<String>(namesSet);
454 * Add the proposed list of container flow configurations to this container
455 * configuration. A validation check on the operation is first run.
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
462 public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
463 Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
464 if (!status.isSuccess()) {
467 if (this.containerFlows.addAll(containerFlowConfigs) == false) {
468 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
470 return new Status(StatusCode.SUCCESS);
473 public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
474 Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
475 if (!status.isSuccess()) {
478 if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
479 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
481 return new Status(StatusCode.SUCCESS);
484 public Status removeContainerFlows(Set<String> names) {
486 if (names == null || names.isEmpty()) {
487 return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
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);
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);
503 if (this.containerFlows.removeAll(toDelete) == false) {
504 return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
506 return new Status(StatusCode.SUCCESS);
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);
514 list.add(new ContainerFlowConfig(conf));
521 public int hashCode() {
522 final int prime = 31;
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());
532 public boolean equals(Object obj) {
539 if (getClass() != obj.getClass()) {
542 ContainerConfig other = (ContainerConfig) obj;
543 if (containerFlows == null) {
544 if (other.containerFlows != null) {
547 } else if (!containerFlows.equals(other.containerFlows)) {
551 if (other.ports != null) {
554 } else if (!ports.equals(other.ports)) {
557 if (container == null) {
558 if (other.container != null) {
561 } else if (!container.equals(other.container)) {
564 if (staticVlan == null) {
565 if (other.staticVlan != null) {
568 } else if (!staticVlan.equals(other.staticVlan)) {
578 * @see java.lang.Object#toString()
581 public String toString() {
582 String vlString = "";
583 if (staticVlan != null) {
584 vlString = staticVlan;
586 return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
590 * Returns whether this Container configuration object has any ports specified
592 * @return true if any port is specified, false otherwise
594 public boolean hasNodeConnectors() {
595 return (ports != null && !ports.isEmpty());
599 * Returns whether this Container configuration object has any flow specs specified
601 * @return true if any flow spec is specified, false otherwise
603 public boolean hasFlowSpecs() {
604 return (containerFlows != null && !containerFlows.isEmpty());
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));
619 private String getContainerRole(boolean admin) {
620 return String.format("%s-%s-%s", containerProfile, container, (admin ? ADMIN_SUFFIX : OPERATOR_SUFFIX));
624 * Return the well known administrator role for this container
626 * @return The administrator role for this container
628 public String getContainerAdminRole() {
629 return getContainerRole(true);
633 * Return the well known operator role for this container
635 * @return The operator role for this container
637 public String getContainerOperatorRole() {
638 return getContainerRole(false);
641 public String getContainerGroupName() {
642 return String.format("%s-%s", containerProfile, container);