Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/com/cloud/dc/DataCenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,6 @@ public enum NetworkType {
String getZoneToken();

boolean isLocalStorageEnabled();

boolean isDynamicRoutingEnabled();
}
8 changes: 5 additions & 3 deletions api/src/com/cloud/network/Network.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static class Service {
public static final Service SecurityGroup = new Service("SecurityGroup");
public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols);
public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter, Capability.RegionLevelVpc, Capability.StretchedL2Subnet);
public static final Service VPCDynamicRouting = new Service("VPCDynamicRouting");

private final String name;
private final Capability[] caps;
Expand Down Expand Up @@ -213,6 +214,7 @@ public static class Capability {
public static final Capability DistributedRouter = new Capability("DistributedRouter");
public static final Capability StretchedL2Subnet = new Capability("StretchedL2Subnet");
public static final Capability RegionLevelVpc = new Capability("RegionLevelVpc");
public static final Capability VPCDynamicRouting = new Capability("VPCDynamicRouting");

private final String name;

Expand Down Expand Up @@ -241,9 +243,9 @@ enum Event {

public enum State {

Allocated("Indicates the network configuration is in allocated but not setup"), Setup("Indicates the network configuration is setup"), Implementing(
"Indicates the network configuration is being implemented"), Implemented("Indicates the network configuration is in use"), Shutdown(
"Indicates the network configuration is being destroyed"), Destroy("Indicates that the network is destroyed");
Allocated("Indicates the network configuration is in allocated but not setup"), Setup("Indicates the network configuration is setup"),
Implementing("Indicates the network configuration is being implemented"), Implemented("Indicates the network configuration is in use"),
Shutdown("Indicates the network configuration is being destroyed"), Destroy("Indicates that the network is destroyed");

protected static final StateMachine2<State, Network.Event, Network> s_fsm = new StateMachine2<State, Network.Event, Network>();

Expand Down
2 changes: 2 additions & 0 deletions api/src/com/cloud/network/PhysicalNetworkServiceProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@ public enum State {
String getUuid();

boolean isNetworkAclServiceProvided();

boolean isDynamicRoutingProvided();
}
329 changes: 329 additions & 0 deletions api/src/com/cloud/network/vpc/OSPFZoneConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package com.cloud.network.vpc;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.net.cidr.BadCIDRException;
import com.cloud.utils.net.cidr.CIDR;

public class OSPFZoneConfig {

private long zoneId;
private Protocol protocol;
private String ospfArea;
private Short helloInterval;
private Short deadInterval;
private Short retransmitInterval;
private Short transitDelay;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abhinandanprateek why did you chose to represent these time values as Short rather than Duration? My issue with Short is there is no notion of units (e.g. seconds, milliseconds) etc. Therefore, the conversion semantics are impossible to determine when just represented as a numeric value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values are not consumed by cloudstack and just passed along to quagga config on VR, where these are interpreted by quagga service. Short naturally constraints the value to a reasonable range.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abhinandanprateek the benefit of Duration is that it carries a unit of measure. This features ensures that any conversions (e.g. milliseconds to seconds) are reliably performed using well-tested code when rendered to the VR configuration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jburwell These values are unused by cloudstack, Short provides a nice range to the values. Since they are passed to VR and used by python scripts, Duration will not give us any added advantage here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abhinandanprateek but short lacks any notion of time units. Is that value in milliseconds, seconds, minutes? Duration provides this resolution and the methods to convert values reliably between units and getting the value as a long (e.g. long getStandardsSeconds(), long getStandardMinutes()).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jburwell lets consider "helloInterval" it is a interval is clear from the name and how it is displayed in UI. Internally it is handled as Short as the MS business logic does not operate on its intrinsic quality of time. Now lets consider that we make it duration as in joda.time pacakge. Now MS business logic need to carry on the overheads of this notion of time by calling the right constructor with additional work required to interpret the parameters and then again doing that conversion when passing this value to virtual router scripts which in turn will dump it in a config file. The tricky part is that this variable conveys notion of time as you rightly put. But the management server business logic need not be aware of this notion of time and can safely transfer the values using Short and thus avoid overheads in conversion back and forth.

private Authentication authentication;
private String password;
private CIDR[] superCIDRList;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this list of values represented as an array and not a List or Set? Is order required? If not, a Set would provide uniqueness guarantees.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uniqness guarantee is not enough by itself, since more complex validations need to run on the CIDR list like the type and overlap , Set would not have provided a major advantage.

private Boolean enabled;

public static final String s_protocol = "protocol";
public static final String s_area = "area";
public static final String s_helloInterval = "hellointerval";
public static final String s_deadInterval = "deadinterval";
public static final String s_retransmitInterval = "retransmitinterval";
public static final String s_transitDelay = "transitdelay";
public static final String s_authentication = "authentication";
public static final String s_superCIDR = "supercidr";
public static final String s_password = "password";
public static final String s_enabled = "enabled";

public Protocol getProtocol() {
return protocol;
}

public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}

public String getOspfArea() {
return ospfArea;
}

public void setOspfArea(String ospfArea) {
this.ospfArea = ospfArea;
}

public Short getHelloInterval() {
return helloInterval;
}

public void setHelloInterval(Short helloInterval) {
this.helloInterval = helloInterval;
}

public Short getDeadInterval() {
return deadInterval;
}

public void setDeadInterval(Short deadInterval) {
this.deadInterval = deadInterval;
}

public Short getRetransmitInterval() {
return retransmitInterval;
}

public void setRetransmitInterval(Short retransmitInterval) {
this.retransmitInterval = retransmitInterval;
}

public Short getTransitDelay() {
return transitDelay;
}

public void setTransitDelay(Short transitDelay) {
this.transitDelay = transitDelay;
}

public Authentication getAuthentication() {
return authentication;
}

public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public CIDR[] getSuperCIDR() {
return superCIDRList;
}

public void setSuperCIDR(CIDR[] superCIDR) {
this.superCIDRList = superCIDR;
}

public Boolean getEnabled() {
return enabled;
}

public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}

public enum Params {
PROTOCOL, AREA, HELLO_INTERVAL, DEAD_INTERVAL, RETRANSMIT_INTERVAL, TRANSIT_DELAY, AUTHENTICATION, SUPER_CIDR, PASSWORD, ENABLED
}

public enum Protocol {
OSPF
}

public enum Authentication {
MD5, PLAIN_TEXT
}

public OSPFZoneConfig() {
}

public void setDefaultValues(final Map<String, String> details) throws BadCIDRException {
this.protocol = details.get(Params.PROTOCOL.name()) == null ? Protocol.OSPF : Protocol.valueOf(details.get(Params.PROTOCOL.name()));
this.ospfArea = details.get(Params.AREA.name()) == null ? "0" : details.get(Params.AREA.name());
this.helloInterval = Short.valueOf(details.get(Params.HELLO_INTERVAL.name()) == null ? "10" : details.get(Params.HELLO_INTERVAL.name()));
this.deadInterval = Short.valueOf(details.get(Params.DEAD_INTERVAL.name()) == null ? "40" : details.get(Params.DEAD_INTERVAL.name()));
this.retransmitInterval = Short.valueOf(details.get(Params.RETRANSMIT_INTERVAL.name()) == null ? "5" : details.get(Params.RETRANSMIT_INTERVAL.name()));
this.transitDelay = Short.valueOf(details.get(Params.TRANSIT_DELAY.name()) == null ? "1" : details.get(Params.TRANSIT_DELAY.name()));
this.authentication = details.get(Params.AUTHENTICATION.name()) == null ? Authentication.MD5 : Authentication.valueOf(details.get(Params.AUTHENTICATION.name()));
this.password = details.get(Params.PASSWORD.name()) == null ? "" : details.get(Params.PASSWORD.name());
this.superCIDRList = details.get(Params.SUPER_CIDR.name()) == null ? new CIDR[0] : NetUtils.convertToCIDR(details.get(Params.SUPER_CIDR.name()).split(","));
this.enabled = details.get(Params.ENABLED.name()) == null ? Boolean.FALSE : Boolean.valueOf(details.get(Params.ENABLED.name()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This ternary operation is repeated throughout this method. Please extract to a common utility method such as getMapValueWithDefault and include unit tests.
  2. Since the value being retrieved is a String, test the value with Strings.isNullOrEmptyto protect against both null and blank values.

Copy link
Contributor

@jburwell jburwell May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a repeating group of the following logic:

details.get(<key>) == null ? <default value> : details.get(<key>)

Please extract to utility method in CloudStack comment (e.g. <K, V> V getOptionalValue(final Map<K, V> map, K key, V default)) with the appropriate unit tests. Also, consider using Map.containsKey to check for key existence. While checking if a value for a key is null is the same, the intent of the code is easier to follow.

}

public void setValues(final String protocol, final String ospfArea, final Short helloInterval, final Short deadInterval, final Short retransmitInterval,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this method and setDefaultValues exist? It feels like these two methods should be replaced by a builder class that sets default values and provides with methods for each property's default value to be overridden. The builder class could be an inner class of OSPFZoneConfig.

final Short transitDelay, final String authentication, final String quaggaPassword, final String superCIDRList, final Boolean enabled) throws BadCIDRException {
if (protocol != null) {
if ((Protocol.OSPF.name().equals(protocol))) {
this.protocol = Protocol.valueOf(protocol);
} else {
throw new InvalidParameterValueException("The quagga protocal can have values of OSPF only " + protocol);
}
}
if (ospfArea != null) {
this.ospfArea = ospfArea;
}
if (helloInterval != null) {
this.helloInterval = helloInterval;
}
if (deadInterval != null) {
this.deadInterval = deadInterval;
}
if (retransmitInterval != null) {
this.retransmitInterval = retransmitInterval;
}
if (transitDelay != null) {
this.transitDelay = transitDelay;
}
if (authentication != null) {
if (Authentication.MD5.name().equals(authentication) || Authentication.PLAIN_TEXT.name().equals(authentication)) {
this.authentication = Authentication.valueOf(authentication);
} else {
throw new InvalidParameterValueException("The quagga authentication can have values of MD5 or PLAIN_TEXT and not " + protocol);
}
}
if (quaggaPassword != null) {
this.password = quaggaPassword;
}
if (superCIDRList != null) {
String[] cidr_list = superCIDRList.split(",");
for (String cidr : cidr_list) {
if (!NetUtils.isValidCIDR(cidr)) {
Copy link
Contributor

@jburwell jburwell May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not provide a static boolean isValid(final String value) method on the CIDR interface?

throw new InvalidParameterValueException("The super CIDR is not a valid cidr " + cidr);
}
}
CIDR[] v_cidr = NetUtils.convertToCIDR(cidr_list);
Copy link
Contributor

@jburwell jburwell May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not provide a static List<CIDR> fromValues(Iterable<String>) factory method on the CIDR interface?

if (!NetUtils.cidrListConsistency(v_cidr)) {
Copy link
Contributor

@jburwell jburwell May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at Line 206 and this line, I am wondering if a CIDR range or collection type would be useful since we operate ordered sets of CIDRs. It may also be a place to encapsulate the creation of a list of CIDR instances from a comma-separated string.

throw new InvalidParameterValueException("The cidr list is not consistent " + Arrays.toString(cidr_list));
}
this.superCIDRList = v_cidr;
}
if (enabled != null) {
this.enabled = enabled;
}
}

public void setValues(Map<String, String> details) throws BadCIDRException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There appears to be a lot of overlap between this method and setDefaultValues. What is the difference between these methods?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setDefaultValues bootstraps the config. I have a feeling that to make the API easy on the user, I have created an unnecessary complexity. Like I assume default values when not provided. Instead this method could have been much cleaner without any assumptions of default and ensuring that all the values are provided all the time.

Protocol protocol = Protocol.valueOf(details.get(Params.PROTOCOL.name()));
String ospfArea = details.get(Params.AREA.name());
Short helloInterval = Short.valueOf(details.get(Params.HELLO_INTERVAL.name()));
Short deadInterval = Short.valueOf(details.get(Params.DEAD_INTERVAL.name()));
Short retransmitInterval = Short.valueOf(details.get(Params.RETRANSMIT_INTERVAL.name()));
Short transitDelay = Short.valueOf(details.get(Params.TRANSIT_DELAY.name()));
Authentication authentication = Authentication.valueOf(details.get(Params.AUTHENTICATION.name()));
String quaggaPassword = details.get(Params.PASSWORD.name());
String superCIDRList = details.get(Params.SUPER_CIDR.name());
Boolean enabled = Boolean.valueOf(details.get(Params.ENABLED.name()));

if (protocol != null) {
if (Protocol.OSPF == protocol) {
this.protocol = protocol;
} else {
throw new InvalidParameterValueException("The quagga protocal can have values of OSPF " + protocol);
}
}
if (ospfArea != null) {
this.ospfArea = ospfArea;
}
if (helloInterval != null) {
this.helloInterval = helloInterval;
}
if (deadInterval != null) {
this.deadInterval = deadInterval;
}
if (retransmitInterval != null) {
this.retransmitInterval = retransmitInterval;
}
if (transitDelay != null) {
this.transitDelay = transitDelay;
}
if (authentication != null) {
if (Authentication.MD5 == authentication || Authentication.PLAIN_TEXT == authentication) {
this.authentication = authentication;
} else {
throw new InvalidParameterValueException("The quagga authentication can have values of MD5 or PLAIN_TEXT and not " + authentication);
}
}
if (quaggaPassword != null) {
this.password = quaggaPassword;
}
if (superCIDRList != null) {
String[] cidr_list = superCIDRList.split(",");
for (String cidr : cidr_list) {
if (!NetUtils.isValidCIDR(cidr)) {
throw new InvalidParameterValueException("The super CIDR is not a valid cidr " + cidr);
}
}
CIDR[] v_cidr = NetUtils.convertToCIDR(cidr_list);
if (!NetUtils.cidrListConsistency(v_cidr)) {
throw new InvalidParameterValueException("The cidr list is not consistent " + Arrays.toString(cidr_list));
}
this.superCIDRList = v_cidr;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 261-273 appear to be a repeat of 199-211. Please to a common method or consider integrating into the CIDR collection class discussed earlier.

if (enabled != null) {
this.enabled = enabled;
}
}

public Map<String, String> getValues() throws BadCIDRException {
Map<String, String> details = new HashMap<String, String>();
if (protocol != null) {
if ((Protocol.OSPF.name().equals(protocol))) {
details.put(Params.PROTOCOL.name(), protocol.name());
} else {
details.put(Params.PROTOCOL.name(), Protocol.OSPF.name());
}
}
if (ospfArea != null) {
details.put(Params.AREA.name(), ospfArea);
}
if (helloInterval != null) {
details.put(Params.HELLO_INTERVAL.name(), helloInterval.toString());
}
if (deadInterval != null) {
details.put(Params.DEAD_INTERVAL.name(), deadInterval.toString());
}
if (retransmitInterval != null) {
details.put(Params.RETRANSMIT_INTERVAL.name(), retransmitInterval.toString());
}
if (transitDelay != null) {
details.put(Params.TRANSIT_DELAY.name(), transitDelay.toString());
}
if (authentication != null) {
if (Authentication.MD5.name().equals(authentication) || Authentication.PLAIN_TEXT.name().equals(authentication)) {
details.put(Params.AUTHENTICATION.name(), authentication.name());
} else {
details.put(Params.AUTHENTICATION.name(), Authentication.MD5.name());
}
}
if (password != null) {
details.put(Params.PASSWORD.name(), password);
}
if (!ArrayUtils.isEmpty(superCIDRList)) {
if (!NetUtils.cidrListConsistency(superCIDRList)) {
throw new InvalidParameterValueException("The cidr list is not consistent " + Arrays.toString(superCIDRList));
}
details.put(Params.SUPER_CIDR.name(), StringUtils.join(superCIDRList, ','));

} else {
details.put(Params.SUPER_CIDR.name(), "200.100.0.0/20");
}
if (enabled != null) {
details.put(Params.ENABLED.name(), enabled.toString());
}

return details;
}

}
1 change: 1 addition & 0 deletions api/src/com/cloud/network/vpc/Vpc.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,5 @@ public enum State {
* @return true if VPC spans multiple zones in the region
*/
boolean isRegionLevelVpc();

}
2 changes: 2 additions & 0 deletions api/src/com/cloud/network/vpc/VpcOffering.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import org.apache.cloudstack.api.InternalIdentity;

public interface VpcOffering extends InternalIdentity, Identity {

public enum State {
Disabled, Enabled
}

public static final String defaultVPCOfferingName = "Default VPC offering";
public static final String defaultVPCNSOfferingName = "Default VPC offering with Netscaler";
public static final String redundantVPCOfferingName = "Redundant VPC offering";
public static final String routedVPCOfferingName = "Default Dynamically Routed VPC offering";

/**
*
Expand Down
Loading