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.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;
32 * Container Configuration Java Object for Container Manager Represents a container
33 * configuration information for Container Manager.
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.
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";
50 private String container;
53 private String staticVlan;
55 @XmlElement(name = "nodeConnectors")
56 private List<String> ports;
58 @XmlElement(name = "flowSpecs")
59 private List<ContainerFlowConfig> containerFlows;
61 public String getContainer() {
65 public void setContainer(String container) {
66 this.container = container;
69 public String getStaticVlan() {
73 public void setStaticVlan(String staticVlan) {
74 this.staticVlan = staticVlan;
77 public List<ContainerFlowConfig> getContainerFlows() {
78 return containerFlows;
81 public void setContainerFlows(List<ContainerFlowConfig> containerFlows) {
82 this.containerFlows = containerFlows;
85 public static long getSerialversionuid() {
86 return serialVersionUID;
89 public static String getRegexname() {
93 public void setPorts(List<String> ports) {
98 * Default constructor needed by Gson.
100 * @return a Default ContainerConfig
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);
110 * Constructor for the ContainerConfig.
113 * Name of the container in this configuration
115 * vlan assigned to this container
117 * the name of the node assigned to the container from this
120 * the list of NodeConnectors on the Node belonging to the container
121 * @return the constructed object
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);
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);
140 * Returns the container name.
142 * @return the container name
144 public String getContainerName() {
149 * Returns the Vlan tag.
151 * @return the Vlan Tag configured for this container configuration
153 public String getVlanTag() {
158 * Returns the configured ports.
160 * @return the string with the list of ports associated to the container on this
163 public List<String> getPorts() {
164 return new ArrayList<String>(ports);
168 * Returns the list of container flows configured for this container
172 public List<ContainerFlowConfig> getContainerFlowConfigs() {
173 return (containerFlows == null || containerFlows.isEmpty()) ? new ArrayList<ContainerFlowConfig>(0)
174 : new ArrayList<ContainerFlowConfig>(containerFlows);
178 * Matches container name against passed parameter.
181 * name of the container to be matched
182 * @return true if the passed argument correspond with the container name in the
185 public boolean matchName(String name) {
186 return this.container.equals(name);
190 * Parse the port list in several NodeConnector descriptor.
192 * @return the list of NodeConnector corresponding to the ports configured
193 * on this configuration
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));
206 * Checks if this is a valid container configuration
208 * @return true, if is valid container configuration, false otherwise
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();
225 * Checks for valid name.
227 * @return true, if successful
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");
236 * Checks for valid ports.
238 * @return true, if successful
240 private Status validatePorts() {
241 return validateNodeConnectors(this.ports);
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);
253 return new Status(StatusCode.SUCCESS);
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));
265 * Checks for valid static vlan.
267 * @return true, if successful
269 private Status validateStaticVlan() {
270 if (staticVlan != null && !staticVlan.trim().isEmpty()) {
273 vl = Short.valueOf(staticVlan);
274 } catch (NumberFormatException e) {
275 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
277 if ((vl < 1) || (vl > 4095)) {
278 return new Status(StatusCode.BADREQUEST, "Static Vlan Value must be between 1 and 4095");
281 return new Status(StatusCode.SUCCESS);
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());
293 return new Status(StatusCode.SUCCESS);
297 * Returns Vlan value in short
299 * @return the Vlan tag
301 public short getStaticVlanValue() {
302 if ((staticVlan == null) || (staticVlan.trim().isEmpty())) {
306 return Short.valueOf(staticVlan);
307 } catch (NumberFormatException e) {
312 public Status addNodeConnectors(List<String> ncList) {
314 Status status = ContainerConfig.validateNodeConnectors(ncList);
315 if (!status.isSuccess()) {
320 ports.addAll(ncList);
321 return new Status(StatusCode.SUCCESS);
324 public Status removeNodeConnectors(List<String> ncList) {
326 Status status = ContainerConfig.validateNodeConnectors(ncList);
327 if (!status.isSuccess()) {
331 if (ports.isEmpty()) {
332 return new Status(StatusCode.BADREQUEST, "The following node connectors are not part of this container: "
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);
341 ports.removeAll(ncList);
342 return new Status(StatusCode.SUCCESS);
345 public Status validateContainerFlowModify(List<ContainerFlowConfig> cFlowConfigs, boolean delete) {
347 if (cFlowConfigs == null || cFlowConfigs.isEmpty()) {
348 return new Status(StatusCode.BADREQUEST, "Invalid Flow Spec configuration(s): null or empty list");
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()));
358 // Name conflict check
359 List<String> existingNames = this.getContainerFlowConfigsNames();
360 List<String> proposedNames = this.getContainerFlowConfigsNames(cFlowConfigs);
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");
368 // Check for overflow
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");
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",
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());
394 * Check for conflicting flow spec match (we only check for strict
395 * equality). Remove this in case (*) is reintroduced
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());
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()));
413 return new Status(StatusCode.SUCCESS);
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);
427 public List<String> getContainerFlowConfigsNames() {
428 return getContainerFlowConfigsNames(this.containerFlows);
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
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
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());
451 return new ArrayList<String>(namesSet);
455 * Add the proposed list of container flow configurations to this container
456 * configuration. A validation check on the operation is first run.
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
463 public Status addContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
464 Status status = this.validateContainerFlowModify(containerFlowConfigs, false);
465 if (!status.isSuccess()) {
468 if (this.containerFlows.addAll(containerFlowConfigs) == false) {
469 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
471 return new Status(StatusCode.SUCCESS);
474 public Status removeContainerFlows(List<ContainerFlowConfig> containerFlowConfigs) {
475 Status status = this.validateContainerFlowModify(containerFlowConfigs, true);
476 if (!status.isSuccess()) {
479 if (this.containerFlows.removeAll(containerFlowConfigs) == false) {
480 return new Status(StatusCode.INTERNALERROR, "Unable to update the flow spec configuration(s)");
482 return new Status(StatusCode.SUCCESS);
485 public Status removeContainerFlows(Set<String> names) {
487 if (names == null || names.isEmpty()) {
488 return new Status(StatusCode.BADREQUEST, "Invalid flow spec names list");
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);
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);
504 if (this.containerFlows.removeAll(toDelete) == false) {
505 return new Status(StatusCode.INTERNALERROR, "Unable to remove the flow spec configuration(s)");
507 return new Status(StatusCode.SUCCESS);
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);
515 list.add(new ContainerFlowConfig(conf));
522 public int hashCode() {
523 final int prime = 31;
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());
533 public boolean equals(Object obj) {
540 if (getClass() != obj.getClass()) {
543 ContainerConfig other = (ContainerConfig) obj;
544 if (containerFlows == null) {
545 if (other.containerFlows != null) {
548 } else if (!containerFlows.equals(other.containerFlows)) {
552 if (other.ports != null) {
555 } else if (!ports.equals(other.ports)) {
558 if (container == null) {
559 if (other.container != null) {
562 } else if (!container.equals(other.container)) {
565 if (staticVlan == null) {
566 if (other.staticVlan != null) {
569 } else if (!staticVlan.equals(other.staticVlan)) {
579 * @see java.lang.Object#toString()
582 public String toString() {
583 String vlString = "";
584 if (staticVlan != null) {
585 vlString = staticVlan;
587 return "container=" + container + ((vlString.equals("") ? "" : " static Vlan=" + vlString)) + " ports=" + ports + " flowspecs=" + containerFlows;
591 * Returns whether this Container configuration object has any ports specified
593 * @return true if any port is specified, false otherwise
595 public boolean hasNodeConnectors() {
596 return (ports != null && !ports.isEmpty());
600 * Returns whether this Container configuration object has any flow specs specified
602 * @return true if any flow spec is specified, false otherwise
604 public boolean hasFlowSpecs() {
605 return (containerFlows != null && !containerFlows.isEmpty());
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));
620 private String getContainerRole(boolean admin) {
621 return String.format("%s-%s-%s", containerProfile, container, (admin ? ADMIN_SUFFIX : OPERATOR_SUFFIX));
625 * Return the well known administrator role for this container
627 * @return The administrator role for this container
629 public String getContainerAdminRole() {
630 return getContainerRole(true);
634 * Return the well known operator role for this container
636 * @return The operator role for this container
638 public String getContainerOperatorRole() {
639 return getContainerRole(false);
642 public String getContainerGroupName() {
643 return String.format("%s-%s", containerProfile, container);