diff --git a/api/docs/apis-network.openshift.io/v1.ClusterNetwork.adoc b/api/docs/apis-network.openshift.io/v1.ClusterNetwork.adoc index 07cbdd805d6b..b4c4cd4b1b52 100644 --- a/api/docs/apis-network.openshift.io/v1.ClusterNetwork.adoc +++ b/api/docs/apis-network.openshift.io/v1.ClusterNetwork.adoc @@ -19,7 +19,10 @@ Expand or mouse-over a field for more information about it. ++++
apiVersion: -hostsubnetlength: +clusterNetworks: +
- CIDR: +hostSubnetLength: +hostsubnetlength:kind:metadata:
annotations: diff --git a/api/docs/oapi/v1.ClusterNetwork.adoc b/api/docs/oapi/v1.ClusterNetwork.adoc index 67baf5d9b27e..76833b56552b 100644 --- a/api/docs/oapi/v1.ClusterNetwork.adoc +++ b/api/docs/oapi/v1.ClusterNetwork.adoc @@ -19,7 +19,10 @@ Expand or mouse-over a field for more information about it. ++++
apiVersion: -hostsubnetlength: +clusterNetworks: +
- CIDR: +hostSubnetLength: +hostsubnetlength:kind:metadata:
annotations: diff --git a/api/protobuf-spec/github_com_openshift_origin_pkg_network_apis_network_v1.proto b/api/protobuf-spec/github_com_openshift_origin_pkg_network_apis_network_v1.proto index 6b08c1927c8b..ad7917a65290 100644 --- a/api/protobuf-spec/github_com_openshift_origin_pkg_network_apis_network_v1.proto +++ b/api/protobuf-spec/github_com_openshift_origin_pkg_network_apis_network_v1.proto @@ -31,6 +31,18 @@ message ClusterNetwork { // PluginName is the name of the network plugin being used optional string pluginName = 5; + + // ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from. + repeated ClusterNetworkEntry clusterNetworks = 6; +} + +// ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips. +message ClusterNetworkEntry { + // CIDR defines the total range of a cluster networks address space. + optional string cidr = 1; + + // HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods. + optional uint32 hostSubnetLength = 2; } // ClusterNetworkList is a collection of ClusterNetworks diff --git a/api/swagger-spec/oapi-v1.json b/api/swagger-spec/oapi-v1.json index 0aa616a12539..7bb4fdcddec0 100644 --- a/api/swagger-spec/oapi-v1.json +++ b/api/swagger-spec/oapi-v1.json @@ -22781,9 +22781,8 @@ "id": "v1.ClusterNetwork", "description": "ClusterNetwork describes the cluster network. There is normally only one object of this type, named \"default\", which is created by the SDN network plugin based on the master configuration when the cluster is brought up for the first time.", "required": [ - "network", - "hostsubnetlength", - "serviceNetwork" + "serviceNetwork", + "clusterNetworks" ], "properties": { "kind": { @@ -22813,6 +22812,31 @@ "pluginName": { "type": "string", "description": "PluginName is the name of the network plugin being used" + }, + "clusterNetworks": { + "type": "array", + "items": { + "$ref": "v1.ClusterNetworkEntry" + }, + "description": "ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from." + } + } + }, + "v1.ClusterNetworkEntry": { + "id": "v1.ClusterNetworkEntry", + "description": "ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips.", + "required": [ + "CIDR", + "hostSubnetLength" + ], + "properties": { + "CIDR": { + "type": "string", + "description": "CIDR defines the total range of a cluster networks address space." + }, + "hostSubnetLength": { + "type": "integer", + "description": "HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods." } } }, diff --git a/api/swagger-spec/openshift-openapi-spec.json b/api/swagger-spec/openshift-openapi-spec.json index 94bc48654bc7..6ffb31970d13 100644 --- a/api/swagger-spec/openshift-openapi-spec.json +++ b/api/swagger-spec/openshift-openapi-spec.json @@ -90742,15 +90742,21 @@ "com.github.openshift.origin.pkg.network.apis.network.v1.ClusterNetwork": { "description": "ClusterNetwork describes the cluster network. There is normally only one object of this type, named \"default\", which is created by the SDN network plugin based on the master configuration when the cluster is brought up for the first time.", "required": [ - "network", - "hostsubnetlength", - "serviceNetwork" + "serviceNetwork", + "clusterNetworks" ], "properties": { "apiVersion": { "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", "type": "string" }, + "clusterNetworks": { + "description": "ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from.", + "type": "array", + "items": { + "$ref": "#/definitions/com.github.openshift.origin.pkg.network.apis.network.v1.ClusterNetworkEntry" + } + }, "hostsubnetlength": { "description": "HostSubnetLength is the number of bits of network to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods", "type": "integer", @@ -90790,6 +90796,24 @@ } ] }, + "com.github.openshift.origin.pkg.network.apis.network.v1.ClusterNetworkEntry": { + "description": "ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips.", + "required": [ + "CIDR", + "hostSubnetLength" + ], + "properties": { + "CIDR": { + "description": "CIDR defines the total range of a cluster networks address space.", + "type": "string" + }, + "hostSubnetLength": { + "description": "HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods.", + "type": "integer", + "format": "int64" + } + } + }, "com.github.openshift.origin.pkg.network.apis.network.v1.ClusterNetworkList": { "description": "ClusterNetworkList is a collection of ClusterNetworks", "required": [ diff --git a/pkg/cmd/server/api/serialization_test.go b/pkg/cmd/server/api/serialization_test.go index c9e31dada854..dc539f7b2e5f 100644 --- a/pkg/cmd/server/api/serialization_test.go +++ b/pkg/cmd/server/api/serialization_test.go @@ -112,13 +112,33 @@ func fuzzInternalObject(t *testing.T, forVersion schema.GroupVersion, item runti obj.NetworkConfig.ServiceNetworkCIDR = "10.0.0.0/24" } } + if len(obj.NetworkConfig.ClusterNetworks) == 0 { + clusterNetwork := []configapi.ClusterNetworkEntry{ + { + CIDR: "10.128.0.0/14", + HostSubnetLength: 9, + }, + } + obj.NetworkConfig.ClusterNetworks = clusterNetwork + } // TODO stop duplicating the conversion in the test. kubeConfig := obj.KubernetesMasterConfig noCloudProvider := kubeConfig != nil && (len(kubeConfig.ControllerArguments["cloud-provider"]) == 0 || kubeConfig.ControllerArguments["cloud-provider"][0] == "") if noCloudProvider && len(obj.NetworkConfig.IngressIPNetworkCIDR) == 0 { cidr := configapi.DefaultIngressIPNetworkCIDR - if !(configapi.CIDRsOverlap(cidr, obj.NetworkConfig.ClusterNetworkCIDR) || configapi.CIDRsOverlap(cidr, obj.NetworkConfig.ServiceNetworkCIDR)) { + setCIDR := true + if configapi.CIDRsOverlap(cidr, obj.NetworkConfig.ServiceNetworkCIDR) { + setCIDR = false + } else { + for _, clusterNetwork := range obj.NetworkConfig.ClusterNetworks { + if configapi.CIDRsOverlap(cidr, clusterNetwork.CIDR) { + setCIDR = false + break + } + } + } + if setCIDR { obj.NetworkConfig.IngressIPNetworkCIDR = cidr } } diff --git a/pkg/cmd/server/api/types.go b/pkg/cmd/server/api/types.go index d509de87d000..8e22abe2e67a 100644 --- a/pkg/cmd/server/api/types.go +++ b/pkg/cmd/server/api/types.go @@ -667,10 +667,12 @@ type UserAgentDenyRule struct { // MasterNetworkConfig to be passed to the compiled in network plugin type MasterNetworkConfig struct { - NetworkPluginName string - ClusterNetworkCIDR string - HostSubnetLength uint32 - ServiceNetworkCIDR string + NetworkPluginName string + DeprecatedClusterNetworkCIDR string + // ClusterNetworks contains a list of cluster networks that defines the global overlay networks L3 space. + ClusterNetworks []ClusterNetworkEntry + DeprecatedHostSubnetLength uint32 + ServiceNetworkCIDR string // ExternalIPNetworkCIDRs controls what values are acceptable for the service external IP field. If empty, no externalIP // may be set. It may contain a list of CIDRs which are checked for access. If a CIDR is prefixed with !, IPs in that // CIDR will be rejected. Rejections will be applied first, then the IP checked against one of the allowed CIDRs. You @@ -683,6 +685,15 @@ type MasterNetworkConfig struct { IngressIPNetworkCIDR string } +// ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs +// reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips. +type ClusterNetworkEntry struct { + // CIDR defines the total range of a cluster networks address space. + CIDR string + // HostSubnetLength gives the number of address bits reserved for pod IPs on each node. + HostSubnetLength uint32 +} + type ImageConfig struct { // Format describes how to determine image names for system components Format string diff --git a/pkg/cmd/server/api/v1/conversions.go b/pkg/cmd/server/api/v1/conversions.go index 13e5323b3a48..0f290b0b6af1 100644 --- a/pkg/cmd/server/api/v1/conversions.go +++ b/pkg/cmd/server/api/v1/conversions.go @@ -90,6 +90,12 @@ func SetDefaults_MasterConfig(obj *MasterConfig) { obj.NetworkConfig.ServiceNetworkCIDR = "10.0.0.0/24" } } + if len(obj.NetworkConfig.ClusterNetworks) == 0 { + obj.NetworkConfig.ClusterNetworks = []ClusterNetworkEntry{{CIDR: obj.NetworkConfig.DeprecatedClusterNetworkCIDR, HostSubnetLength: obj.NetworkConfig.DeprecatedHostSubnetLength}} + + obj.NetworkConfig.DeprecatedClusterNetworkCIDR = "" + obj.NetworkConfig.DeprecatedHostSubnetLength = 0 + } // TODO Detect cloud provider when not using built-in kubernetes kubeConfig := obj.KubernetesMasterConfig @@ -97,7 +103,18 @@ func SetDefaults_MasterConfig(obj *MasterConfig) { if noCloudProvider && len(obj.NetworkConfig.IngressIPNetworkCIDR) == 0 { cidr := internal.DefaultIngressIPNetworkCIDR - if !(internal.CIDRsOverlap(cidr, obj.NetworkConfig.ClusterNetworkCIDR) || internal.CIDRsOverlap(cidr, obj.NetworkConfig.ServiceNetworkCIDR)) { + cidrOverlap := false + if internal.CIDRsOverlap(cidr, obj.NetworkConfig.ServiceNetworkCIDR) { + cidrOverlap = true + } else { + for _, entry := range obj.NetworkConfig.ClusterNetworks { + if internal.CIDRsOverlap(cidr, entry.CIDR) { + cidrOverlap = true + break + } + } + } + if !cidrOverlap { obj.NetworkConfig.IngressIPNetworkCIDR = cidr } } diff --git a/pkg/cmd/server/api/v1/swagger_doc.go b/pkg/cmd/server/api/v1/swagger_doc.go index 0d2f09110ae6..360ce5095722 100644 --- a/pkg/cmd/server/api/v1/swagger_doc.go +++ b/pkg/cmd/server/api/v1/swagger_doc.go @@ -145,6 +145,16 @@ func (ClientConnectionOverrides) SwaggerDoc() map[string]string { return map_ClientConnectionOverrides } +var map_ClusterNetworkEntry = map[string]string{ + "": "ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips.", + "cidr": "CIDR defines the total range of a cluster networks address space.", + "hostSubnetLength": "HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pod.", +} + +func (ClusterNetworkEntry) SwaggerDoc() map[string]string { + return map_ClusterNetworkEntry +} + var map_ControllerConfig = map[string]string{ "": "ControllerConfig holds configuration values for controllers", "election": "Election defines the configuration for electing a controller instance to make changes to the cluster. If unspecified, the ControllerTTL value is checked to determine whether the legacy direct etcd election code will be used.", @@ -536,8 +546,9 @@ func (MasterConfig) SwaggerDoc() map[string]string { var map_MasterNetworkConfig = map[string]string{ "": "MasterNetworkConfig to be passed to the compiled in network plugin", "networkPluginName": "NetworkPluginName is the name of the network plugin to use", - "clusterNetworkCIDR": "ClusterNetworkCIDR is the CIDR string to specify the global overlay network's L3 space", - "hostSubnetLength": "HostSubnetLength is the number of bits to allocate to each host's subnet e.g. 8 would mean a /24 network on the host", + "clusterNetworkCIDR": "ClusterNetworkCIDR is the CIDR string to specify the global overlay network's L3 space. Deprecated, but maintained for backwards compatibility, use ClusterNetworks instead.", + "clusterNetworks": "ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from. If this is specified, then ClusterNetworkCIDR and HostSubnetLength may not be set.", + "hostSubnetLength": "HostSubnetLength is the number of bits to allocate to each host's subnet e.g. 8 would mean a /24 network on the host. Deprecated, but maintained for backwards compatibility, use ClusterNetworks instead.", "serviceNetworkCIDR": "ServiceNetwork is the CIDR string to specify the service networks", "externalIPNetworkCIDRs": "ExternalIPNetworkCIDRs controls what values are acceptable for the service external IP field. If empty, no externalIP may be set. It may contain a list of CIDRs which are checked for access. If a CIDR is prefixed with !, IPs in that CIDR will be rejected. Rejections will be applied first, then the IP checked against one of the allowed CIDRs. You should ensure this range does not overlap with your nodes, pods, or service CIDRs for security reasons.", "ingressIPNetworkCIDR": "IngressIPNetworkCIDR controls the range to assign ingress ips from for services of type LoadBalancer on bare metal. If empty, ingress ips will not be assigned. It may contain a single CIDR that will be allocated from. For security reasons, you should ensure that this range does not overlap with the CIDRs reserved for external ips, nodes, pods, or services.", diff --git a/pkg/cmd/server/api/v1/types.go b/pkg/cmd/server/api/v1/types.go index cce8ab74ee23..f13b61ddd9ef 100644 --- a/pkg/cmd/server/api/v1/types.go +++ b/pkg/cmd/server/api/v1/types.go @@ -543,10 +543,12 @@ type RoutingConfig struct { type MasterNetworkConfig struct { // NetworkPluginName is the name of the network plugin to use NetworkPluginName string `json:"networkPluginName"` - // ClusterNetworkCIDR is the CIDR string to specify the global overlay network's L3 space - ClusterNetworkCIDR string `json:"clusterNetworkCIDR"` - // HostSubnetLength is the number of bits to allocate to each host's subnet e.g. 8 would mean a /24 network on the host - HostSubnetLength uint32 `json:"hostSubnetLength"` + // ClusterNetworkCIDR is the CIDR string to specify the global overlay network's L3 space. Deprecated, but maintained for backwards compatibility, use ClusterNetworks instead. + DeprecatedClusterNetworkCIDR string `json:"clusterNetworkCIDR,omitempty"` + // ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from. If this is specified, then ClusterNetworkCIDR and HostSubnetLength may not be set. + ClusterNetworks []ClusterNetworkEntry `json:"clusterNetworks"` + // HostSubnetLength is the number of bits to allocate to each host's subnet e.g. 8 would mean a /24 network on the host. Deprecated, but maintained for backwards compatibility, use ClusterNetworks instead. + DeprecatedHostSubnetLength uint32 `json:"hostSubnetLength,omitempty"` // ServiceNetwork is the CIDR string to specify the service networks ServiceNetworkCIDR string `json:"serviceNetworkCIDR"` // ExternalIPNetworkCIDRs controls what values are acceptable for the service external IP field. If empty, no externalIP @@ -561,6 +563,14 @@ type MasterNetworkConfig struct { IngressIPNetworkCIDR string `json:"ingressIPNetworkCIDR"` } +// ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips. +type ClusterNetworkEntry struct { + // CIDR defines the total range of a cluster networks address space. + CIDR string `json:"cidr"` + // HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pod. + HostSubnetLength uint32 `json:"hostSubnetLength"` +} + // ImageConfig holds the necessary configuration options for building image names for system components type ImageConfig struct { // Format is the format of the name to be built for the system component diff --git a/pkg/cmd/server/api/v1/types_test.go b/pkg/cmd/server/api/v1/types_test.go index 65332dda4411..45b824e71cb9 100644 --- a/pkg/cmd/server/api/v1/types_test.go +++ b/pkg/cmd/server/api/v1/types_test.go @@ -220,9 +220,8 @@ masterClients: openshiftLoopbackKubeConfig: "" masterPublicURL: "" networkConfig: - clusterNetworkCIDR: "" + clusterNetworks: null externalIPNetworkCIDRs: null - hostSubnetLength: 0 ingressIPNetworkCIDR: "" networkPluginName: "" serviceNetworkCIDR: "" diff --git a/pkg/cmd/server/api/v1/zz_generated.deepcopy.go b/pkg/cmd/server/api/v1/zz_generated.deepcopy.go index 42a072a3d8dc..0fa110f43af1 100644 --- a/pkg/cmd/server/api/v1/zz_generated.deepcopy.go +++ b/pkg/cmd/server/api/v1/zz_generated.deepcopy.go @@ -31,6 +31,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_BasicAuthPasswordIdentityProvider, InType: reflect.TypeOf(&BasicAuthPasswordIdentityProvider{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_CertInfo, InType: reflect.TypeOf(&CertInfo{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ClientConnectionOverrides, InType: reflect.TypeOf(&ClientConnectionOverrides{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ClusterNetworkEntry, InType: reflect.TypeOf(&ClusterNetworkEntry{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ControllerConfig, InType: reflect.TypeOf(&ControllerConfig{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ControllerElectionConfig, InType: reflect.TypeOf(&ControllerElectionConfig{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_DNSConfig, InType: reflect.TypeOf(&DNSConfig{})}, @@ -295,6 +296,16 @@ func DeepCopy_v1_ClientConnectionOverrides(in interface{}, out interface{}, c *c } } +// DeepCopy_v1_ClusterNetworkEntry is an autogenerated deepcopy function. +func DeepCopy_v1_ClusterNetworkEntry(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*ClusterNetworkEntry) + out := out.(*ClusterNetworkEntry) + *out = *in + return nil + } +} + // DeepCopy_v1_ControllerConfig is an autogenerated deepcopy function. func DeepCopy_v1_ControllerConfig(in interface{}, out interface{}, c *conversion.Cloner) error { { @@ -888,6 +899,11 @@ func DeepCopy_v1_MasterNetworkConfig(in interface{}, out interface{}, c *convers in := in.(*MasterNetworkConfig) out := out.(*MasterNetworkConfig) *out = *in + if in.ClusterNetworks != nil { + in, out := &in.ClusterNetworks, &out.ClusterNetworks + *out = make([]ClusterNetworkEntry, len(*in)) + copy(*out, *in) + } if in.ExternalIPNetworkCIDRs != nil { in, out := &in.ExternalIPNetworkCIDRs, &out.ExternalIPNetworkCIDRs *out = make([]string, len(*in)) diff --git a/pkg/cmd/server/api/validation/master.go b/pkg/cmd/server/api/validation/master.go index 2e2d913091af..236ea339135d 100644 --- a/pkg/cmd/server/api/validation/master.go +++ b/pkg/cmd/server/api/validation/master.go @@ -165,6 +165,7 @@ func ValidateMasterConfig(config *api.MasterConfig, fldPath *field.Path) Validat } validationResults.AddErrors(ValidateIngressIPNetworkCIDR(config, fldPath.Child("networkConfig", "ingressIPNetworkCIDR").Index(0))...) + validationResults.Append(ValidateDeprecatedClusterNetworkConfig(config, fldPath.Child("networkConfig"))) validationResults.AddErrors(ValidateKubeConfig(config.MasterClients.OpenShiftLoopbackKubeConfig, fldPath.Child("masterClients", "openShiftLoopbackKubeConfig"))...) @@ -830,8 +831,10 @@ func ValidateIngressIPNetworkCIDR(config *api.MasterConfig, fldPath *field.Path) noCloudProvider := kubeConfig != nil && (len(kubeConfig.ControllerArguments["cloud-provider"]) == 0 || kubeConfig.ControllerArguments["cloud-provider"][0] == "") if noCloudProvider { - if api.CIDRsOverlap(cidr, config.NetworkConfig.ClusterNetworkCIDR) { - addError("conflicts with cluster network CIDR") + for _, entry := range config.NetworkConfig.ClusterNetworks { + if api.CIDRsOverlap(cidr, entry.CIDR) { + addError(fmt.Sprintf("conflicts with cluster network CIDR: %s", entry.CIDR)) + } } if api.CIDRsOverlap(cidr, config.NetworkConfig.ServiceNetworkCIDR) { addError("conflicts with service network CIDR") @@ -842,3 +845,15 @@ func ValidateIngressIPNetworkCIDR(config *api.MasterConfig, fldPath *field.Path) return } + +func ValidateDeprecatedClusterNetworkConfig(config *api.MasterConfig, fldPath *field.Path) ValidationResults { + validationResults := ValidationResults{} + + if len(config.NetworkConfig.ClusterNetworks) > 0 && config.NetworkConfig.DeprecatedHostSubnetLength != 0 { + validationResults.AddErrors(field.Invalid(fldPath.Child("hostSubnetLength"), config.NetworkConfig.DeprecatedHostSubnetLength, "cannot set hostSubnetLength and clusterNetworks, please use clusterNetworks")) + } + if len(config.NetworkConfig.ClusterNetworks) > 0 && config.NetworkConfig.DeprecatedClusterNetworkCIDR != "" { + validationResults.AddErrors(field.Invalid(fldPath.Child("clusterNetworkCIDR"), config.NetworkConfig.DeprecatedClusterNetworkCIDR, "cannot set clusterNetworkCIDR and clusterNetworks, please use clusterNetworks")) + } + return validationResults +} diff --git a/pkg/cmd/server/api/validation/master_test.go b/pkg/cmd/server/api/validation/master_test.go index 071b948b00db..834492cf5825 100644 --- a/pkg/cmd/server/api/validation/master_test.go +++ b/pkg/cmd/server/api/validation/master_test.go @@ -495,7 +495,11 @@ func TestValidateIngressIPNetworkCIDR(t *testing.T) { NetworkConfig: configapi.MasterNetworkConfig{ IngressIPNetworkCIDR: test.cidr, ServiceNetworkCIDR: test.serviceCIDR, - ClusterNetworkCIDR: test.clusterCIDR, + ClusterNetworks: []configapi.ClusterNetworkEntry{ + { + CIDR: test.clusterCIDR, + }, + }, }, } errors := ValidateIngressIPNetworkCIDR(config, nil) diff --git a/pkg/cmd/server/api/zz_generated.deepcopy.go b/pkg/cmd/server/api/zz_generated.deepcopy.go index 07d5e7a92dd9..c827e8b6957c 100644 --- a/pkg/cmd/server/api/zz_generated.deepcopy.go +++ b/pkg/cmd/server/api/zz_generated.deepcopy.go @@ -31,6 +31,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_BasicAuthPasswordIdentityProvider, InType: reflect.TypeOf(&BasicAuthPasswordIdentityProvider{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_CertInfo, InType: reflect.TypeOf(&CertInfo{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClientConnectionOverrides, InType: reflect.TypeOf(&ClientConnectionOverrides{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ClusterNetworkEntry, InType: reflect.TypeOf(&ClusterNetworkEntry{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ControllerConfig, InType: reflect.TypeOf(&ControllerConfig{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ControllerElectionConfig, InType: reflect.TypeOf(&ControllerElectionConfig{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_DNSConfig, InType: reflect.TypeOf(&DNSConfig{})}, @@ -301,6 +302,16 @@ func DeepCopy_api_ClientConnectionOverrides(in interface{}, out interface{}, c * } } +// DeepCopy_api_ClusterNetworkEntry is an autogenerated deepcopy function. +func DeepCopy_api_ClusterNetworkEntry(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*ClusterNetworkEntry) + out := out.(*ClusterNetworkEntry) + *out = *in + return nil + } +} + // DeepCopy_api_ControllerConfig is an autogenerated deepcopy function. func DeepCopy_api_ControllerConfig(in interface{}, out interface{}, c *conversion.Cloner) error { { @@ -889,6 +900,11 @@ func DeepCopy_api_MasterNetworkConfig(in interface{}, out interface{}, c *conver in := in.(*MasterNetworkConfig) out := out.(*MasterNetworkConfig) *out = *in + if in.ClusterNetworks != nil { + in, out := &in.ClusterNetworks, &out.ClusterNetworks + *out = make([]ClusterNetworkEntry, len(*in)) + copy(*out, *in) + } if in.ExternalIPNetworkCIDRs != nil { in, out := &in.ExternalIPNetworkCIDRs, &out.ExternalIPNetworkCIDRs *out = make([]string, len(*in)) diff --git a/pkg/cmd/server/origin/master_config.go b/pkg/cmd/server/origin/master_config.go index b029a4280f60..c1e8d89b8dec 100644 --- a/pkg/cmd/server/origin/master_config.go +++ b/pkg/cmd/server/origin/master_config.go @@ -654,7 +654,12 @@ func newAdmissionChain(pluginNames []string, admissionConfigFilename string, plu case serviceadmit.RestrictedEndpointsPluginName: // we need to set some customer parameters, so create by hand - restrictedNetworks, err := serviceadmit.ParseSimpleCIDRRules([]string{options.NetworkConfig.ClusterNetworkCIDR, options.NetworkConfig.ServiceNetworkCIDR}) + var restricted []string + restricted = append(restricted, options.NetworkConfig.ServiceNetworkCIDR) + for _, cidr := range options.NetworkConfig.ClusterNetworks { + restricted = append(restricted, cidr.CIDR) + } + restrictedNetworks, err := serviceadmit.ParseSimpleCIDRRules(restricted) if err != nil { // should have been caught with validation return nil, err diff --git a/pkg/cmd/server/start/master_args.go b/pkg/cmd/server/start/master_args.go index fe904c6822be..301b7ca87f14 100644 --- a/pkg/cmd/server/start/master_args.go +++ b/pkg/cmd/server/start/master_args.go @@ -294,9 +294,13 @@ func (args MasterArgs) BuildSerializeableMasterConfig() (*configapi.MasterConfig }, NetworkConfig: configapi.MasterNetworkConfig{ - NetworkPluginName: args.NetworkArgs.NetworkPluginName, - ClusterNetworkCIDR: args.NetworkArgs.ClusterNetworkCIDR, - HostSubnetLength: args.NetworkArgs.HostSubnetLength, + NetworkPluginName: args.NetworkArgs.NetworkPluginName, + ClusterNetworks: []configapi.ClusterNetworkEntry{ + { + CIDR: args.NetworkArgs.ClusterNetworkCIDR, + HostSubnetLength: args.NetworkArgs.HostSubnetLength, + }, + }, ServiceNetworkCIDR: args.NetworkArgs.ServiceNetworkCIDR, }, diff --git a/pkg/network/apis/network/types.go b/pkg/network/apis/network/types.go index 7a70eb977f5f..a243dfb0974d 100644 --- a/pkg/network/apis/network/types.go +++ b/pkg/network/apis/network/types.go @@ -17,12 +17,18 @@ type ClusterNetwork struct { metav1.TypeMeta metav1.ObjectMeta + ClusterNetworks []ClusterNetworkEntry Network string HostSubnetLength uint32 ServiceNetwork string PluginName string } +type ClusterNetworkEntry struct { + CIDR string + HostSubnetLength uint32 +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type ClusterNetworkList struct { diff --git a/pkg/network/apis/network/v1/generated.pb.go b/pkg/network/apis/network/v1/generated.pb.go index b5cacf7954fe..27c6d3da1d3d 100644 --- a/pkg/network/apis/network/v1/generated.pb.go +++ b/pkg/network/apis/network/v1/generated.pb.go @@ -10,6 +10,7 @@ It has these top-level messages: ClusterNetwork + ClusterNetworkEntry ClusterNetworkList EgressNetworkPolicy EgressNetworkPolicyList @@ -47,48 +48,53 @@ func (m *ClusterNetwork) Reset() { *m = ClusterNetwork{} } func (*ClusterNetwork) ProtoMessage() {} func (*ClusterNetwork) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } +func (m *ClusterNetworkEntry) Reset() { *m = ClusterNetworkEntry{} } +func (*ClusterNetworkEntry) ProtoMessage() {} +func (*ClusterNetworkEntry) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} } + func (m *ClusterNetworkList) Reset() { *m = ClusterNetworkList{} } func (*ClusterNetworkList) ProtoMessage() {} -func (*ClusterNetworkList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} } +func (*ClusterNetworkList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{2} } func (m *EgressNetworkPolicy) Reset() { *m = EgressNetworkPolicy{} } func (*EgressNetworkPolicy) ProtoMessage() {} -func (*EgressNetworkPolicy) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{2} } +func (*EgressNetworkPolicy) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{3} } func (m *EgressNetworkPolicyList) Reset() { *m = EgressNetworkPolicyList{} } func (*EgressNetworkPolicyList) ProtoMessage() {} -func (*EgressNetworkPolicyList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{3} } +func (*EgressNetworkPolicyList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{4} } func (m *EgressNetworkPolicyPeer) Reset() { *m = EgressNetworkPolicyPeer{} } func (*EgressNetworkPolicyPeer) ProtoMessage() {} -func (*EgressNetworkPolicyPeer) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{4} } +func (*EgressNetworkPolicyPeer) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{5} } func (m *EgressNetworkPolicyRule) Reset() { *m = EgressNetworkPolicyRule{} } func (*EgressNetworkPolicyRule) ProtoMessage() {} -func (*EgressNetworkPolicyRule) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{5} } +func (*EgressNetworkPolicyRule) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{6} } func (m *EgressNetworkPolicySpec) Reset() { *m = EgressNetworkPolicySpec{} } func (*EgressNetworkPolicySpec) ProtoMessage() {} -func (*EgressNetworkPolicySpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{6} } +func (*EgressNetworkPolicySpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{7} } func (m *HostSubnet) Reset() { *m = HostSubnet{} } func (*HostSubnet) ProtoMessage() {} -func (*HostSubnet) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{7} } +func (*HostSubnet) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{8} } func (m *HostSubnetList) Reset() { *m = HostSubnetList{} } func (*HostSubnetList) ProtoMessage() {} -func (*HostSubnetList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{8} } +func (*HostSubnetList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{9} } func (m *NetNamespace) Reset() { *m = NetNamespace{} } func (*NetNamespace) ProtoMessage() {} -func (*NetNamespace) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{9} } +func (*NetNamespace) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{10} } func (m *NetNamespaceList) Reset() { *m = NetNamespaceList{} } func (*NetNamespaceList) ProtoMessage() {} -func (*NetNamespaceList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{10} } +func (*NetNamespaceList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{11} } func init() { proto.RegisterType((*ClusterNetwork)(nil), "github.com.openshift.origin.pkg.network.apis.network.v1.ClusterNetwork") + proto.RegisterType((*ClusterNetworkEntry)(nil), "github.com.openshift.origin.pkg.network.apis.network.v1.ClusterNetworkEntry") proto.RegisterType((*ClusterNetworkList)(nil), "github.com.openshift.origin.pkg.network.apis.network.v1.ClusterNetworkList") proto.RegisterType((*EgressNetworkPolicy)(nil), "github.com.openshift.origin.pkg.network.apis.network.v1.EgressNetworkPolicy") proto.RegisterType((*EgressNetworkPolicyList)(nil), "github.com.openshift.origin.pkg.network.apis.network.v1.EgressNetworkPolicyList") @@ -138,6 +144,43 @@ func (m *ClusterNetwork) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintGenerated(dAtA, i, uint64(len(m.PluginName))) i += copy(dAtA[i:], m.PluginName) + if len(m.ClusterNetworks) > 0 { + for _, msg := range m.ClusterNetworks { + dAtA[i] = 0x32 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *ClusterNetworkEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ClusterNetworkEntry) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CIDR))) + i += copy(dAtA[i:], m.CIDR) + dAtA[i] = 0x10 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.HostSubnetLength)) return i, nil } @@ -523,6 +566,21 @@ func (m *ClusterNetwork) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.PluginName) n += 1 + l + sovGenerated(uint64(l)) + if len(m.ClusterNetworks) > 0 { + for _, e := range m.ClusterNetworks { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *ClusterNetworkEntry) Size() (n int) { + var l int + _ = l + l = len(m.CIDR) + n += 1 + l + sovGenerated(uint64(l)) + n += 1 + sovGenerated(uint64(m.HostSubnetLength)) return n } @@ -672,6 +730,18 @@ func (this *ClusterNetwork) String() string { `HostSubnetLength:` + fmt.Sprintf("%v", this.HostSubnetLength) + `,`, `ServiceNetwork:` + fmt.Sprintf("%v", this.ServiceNetwork) + `,`, `PluginName:` + fmt.Sprintf("%v", this.PluginName) + `,`, + `ClusterNetworks:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ClusterNetworks), "ClusterNetworkEntry", "ClusterNetworkEntry", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *ClusterNetworkEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ClusterNetworkEntry{`, + `CIDR:` + fmt.Sprintf("%v", this.CIDR) + `,`, + `HostSubnetLength:` + fmt.Sprintf("%v", this.HostSubnetLength) + `,`, `}`, }, "") return s @@ -961,6 +1031,135 @@ func (m *ClusterNetwork) Unmarshal(dAtA []byte) error { } m.PluginName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterNetworks", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterNetworks = append(m.ClusterNetworks, ClusterNetworkEntry{}) + if err := m.ClusterNetworks[len(m.ClusterNetworks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClusterNetworkEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ClusterNetworkEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClusterNetworkEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CIDR", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CIDR = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HostSubnetLength", wireType) + } + m.HostSubnetLength = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HostSubnetLength |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2239,58 +2438,62 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 835 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x41, 0x6f, 0xe3, 0x44, - 0x14, 0xce, 0x64, 0xd3, 0xee, 0x76, 0xda, 0x0d, 0x95, 0x41, 0x60, 0x15, 0xc9, 0x8d, 0x8c, 0x84, - 0x82, 0x10, 0x36, 0x2d, 0x08, 0xf6, 0x04, 0x92, 0xdb, 0xd5, 0x6e, 0xa5, 0x12, 0xa2, 0xc9, 0x9e, - 0x10, 0x07, 0x1c, 0xe7, 0xad, 0x33, 0x1b, 0xc7, 0x63, 0x3c, 0xe3, 0x40, 0x2e, 0x88, 0x9f, 0x80, - 0xc4, 0x1f, 0xe2, 0x98, 0xe3, 0xde, 0xe8, 0x01, 0x55, 0xdb, 0x70, 0x83, 0x7f, 0xc0, 0x09, 0xcd, - 0x78, 0xe2, 0x24, 0x8d, 0x23, 0x50, 0xb4, 0xcd, 0x29, 0x19, 0x7f, 0xef, 0x7b, 0xef, 0x7b, 0xef, - 0xcd, 0x7b, 0x83, 0x9f, 0x84, 0x54, 0xf4, 0xb3, 0xae, 0x13, 0xb0, 0xa1, 0xcb, 0x12, 0x88, 0x79, - 0x9f, 0x3e, 0x17, 0x2e, 0x4b, 0x69, 0x48, 0x63, 0x37, 0x19, 0x84, 0x6e, 0x0c, 0xe2, 0x07, 0x96, - 0x0e, 0x5c, 0x3f, 0xa1, 0xbc, 0x38, 0x8c, 0x4e, 0xdc, 0x10, 0x62, 0x48, 0x7d, 0x01, 0x3d, 0x27, - 0x49, 0x99, 0x60, 0xc6, 0xe7, 0x73, 0x47, 0x4e, 0xe1, 0xc8, 0xc9, 0x1d, 0x39, 0xc9, 0x20, 0x74, - 0x34, 0xd7, 0x91, 0x8e, 0x8a, 0xc3, 0xe8, 0xe4, 0xe8, 0xa3, 0x05, 0x05, 0x21, 0x0b, 0x99, 0xab, - 0xfc, 0x75, 0xb3, 0xe7, 0xea, 0xa4, 0x0e, 0xea, 0x5f, 0x1e, 0xe7, 0xe8, 0xd3, 0xc1, 0x23, 0xee, - 0x50, 0x26, 0xf5, 0x0c, 0xfd, 0xa0, 0x4f, 0x63, 0x48, 0xc7, 0x4a, 0xa8, 0x12, 0x38, 0x04, 0xe1, - 0x97, 0xa8, 0x3b, 0x72, 0xd7, 0xb1, 0xd2, 0x2c, 0x16, 0x74, 0x08, 0x2b, 0x84, 0xcf, 0xfe, 0x8b, - 0xc0, 0x83, 0x3e, 0x0c, 0xfd, 0x15, 0xde, 0x27, 0xeb, 0x78, 0x99, 0xa0, 0x91, 0x4b, 0x63, 0xc1, - 0x45, 0x7a, 0x9b, 0x64, 0xff, 0x5e, 0xc5, 0xf5, 0xb3, 0x28, 0xe3, 0x02, 0xd2, 0x56, 0x5e, 0x18, - 0xe3, 0x3b, 0xfc, 0x40, 0xe6, 0xd2, 0xf3, 0x85, 0x6f, 0xa2, 0x06, 0x6a, 0xee, 0x9f, 0x7e, 0xec, - 0xe4, 0xae, 0x9d, 0x45, 0xd7, 0xaa, 0xb2, 0xaa, 0xa2, 0xd2, 0xda, 0x19, 0x9d, 0x38, 0x5f, 0x77, - 0x5f, 0x40, 0x20, 0xbe, 0x02, 0xe1, 0x7b, 0xc6, 0xe4, 0xfa, 0xb8, 0x32, 0xbd, 0x3e, 0xc6, 0xf3, - 0x6f, 0xa4, 0xf0, 0x6a, 0x7c, 0x80, 0xef, 0xeb, 0x2e, 0x98, 0xd5, 0x06, 0x6a, 0xee, 0x79, 0x6f, - 0x68, 0xf3, 0xfb, 0x5a, 0x03, 0x99, 0xe1, 0xc6, 0x39, 0x3e, 0xec, 0x33, 0x2e, 0x78, 0xd6, 0x8d, - 0x41, 0x44, 0x10, 0x87, 0xa2, 0x6f, 0xde, 0x6b, 0xa0, 0xe6, 0x43, 0xcf, 0xd4, 0x9c, 0xc3, 0xa7, - 0x8c, 0x8b, 0x8e, 0xc2, 0x2f, 0x15, 0x4e, 0x56, 0x18, 0xc6, 0x17, 0xb8, 0xce, 0x21, 0x1d, 0xd1, - 0x00, 0x74, 0x00, 0xb3, 0xa6, 0xe2, 0xbe, 0xad, 0x7d, 0xd4, 0x3b, 0x4b, 0x28, 0xb9, 0x65, 0x6d, - 0x9c, 0x62, 0x9c, 0x44, 0x59, 0x48, 0xe3, 0x96, 0x3f, 0x04, 0x73, 0x47, 0x71, 0x8b, 0x14, 0xdb, - 0x05, 0x42, 0x16, 0xac, 0xec, 0x57, 0x08, 0x1b, 0xcb, 0x95, 0xbd, 0xa4, 0x5c, 0x18, 0xdf, 0xae, - 0x54, 0xd7, 0xf9, 0x7f, 0xd5, 0x95, 0x6c, 0x55, 0xdb, 0x43, 0x1d, 0xf8, 0xc1, 0xec, 0xcb, 0x42, - 0x65, 0x23, 0xbc, 0x43, 0x05, 0x0c, 0xb9, 0x59, 0x6d, 0xdc, 0x6b, 0xee, 0x9f, 0x3e, 0x71, 0x36, - 0x1c, 0x0d, 0x67, 0x59, 0xb9, 0xf7, 0x50, 0xc7, 0xdc, 0xb9, 0x90, 0xde, 0x49, 0x1e, 0xc4, 0xfe, - 0x1b, 0xe1, 0x37, 0x1f, 0x87, 0x29, 0x70, 0xae, 0xed, 0xda, 0x2c, 0xa2, 0xc1, 0x78, 0x0b, 0x37, - 0x28, 0xc5, 0x35, 0x9e, 0x40, 0xa0, 0xae, 0xcf, 0xfe, 0x69, 0x7b, 0xe3, 0x34, 0x4b, 0xd4, 0x77, - 0x12, 0x08, 0xbc, 0x03, 0x1d, 0xbd, 0x26, 0x4f, 0x44, 0xc5, 0xb2, 0xff, 0x42, 0xf8, 0x9d, 0x12, - 0xfb, 0x2d, 0x74, 0xf5, 0xfb, 0xe5, 0xae, 0x5e, 0xbe, 0xce, 0x74, 0xd7, 0xb4, 0xf6, 0xa7, 0xd2, - 0x5c, 0xdb, 0x00, 0xa9, 0xf1, 0x08, 0x1f, 0x04, 0xb4, 0x97, 0x76, 0x20, 0x82, 0x40, 0xb0, 0x54, - 0xe5, 0xbb, 0xe7, 0xbd, 0xa5, 0xdd, 0x1c, 0x9c, 0x5d, 0x9c, 0x93, 0x19, 0x46, 0x96, 0x2c, 0xe5, - 0xdc, 0xf7, 0x62, 0xae, 0x66, 0xe8, 0xd6, 0xdc, 0x9f, 0xb7, 0x3a, 0x6a, 0x80, 0x66, 0xb8, 0x3d, - 0x29, 0x2f, 0x36, 0xc9, 0x22, 0x30, 0xbe, 0xc4, 0x35, 0x31, 0x4e, 0x40, 0x07, 0xfe, 0x70, 0xd6, - 0xaa, 0x67, 0xe3, 0x04, 0xfe, 0xb9, 0x3e, 0x7e, 0x77, 0x0d, 0x4d, 0xc2, 0x44, 0x11, 0x8d, 0x08, - 0x57, 0x05, 0xbb, 0x8b, 0xbb, 0x23, 0xeb, 0xe3, 0x61, 0x2d, 0xa8, 0xfa, 0x8c, 0x91, 0xaa, 0x60, - 0xf6, 0xaf, 0xe5, 0xa9, 0xc8, 0x9b, 0x65, 0xfc, 0x88, 0x77, 0x41, 0x41, 0x26, 0x52, 0xad, 0x7d, - 0xad, 0x6a, 0x64, 0xd6, 0x5e, 0x5d, 0xab, 0xd9, 0xcd, 0x0d, 0x88, 0x8e, 0x27, 0xd7, 0x13, 0x9e, - 0x6f, 0xce, 0x2d, 0x8c, 0x6c, 0x03, 0xd7, 0xe4, 0x5e, 0xd6, 0x9d, 0x2f, 0x06, 0x4c, 0x6a, 0x20, - 0x0a, 0x31, 0xde, 0xc7, 0xbb, 0xf2, 0xf7, 0xa2, 0xad, 0x36, 0xfc, 0xde, 0x5c, 0xfa, 0x53, 0xf5, - 0x95, 0x68, 0x54, 0xda, 0xe5, 0xdb, 0x5d, 0x6f, 0xf1, 0xc2, 0x2e, 0xcf, 0x85, 0x68, 0xd4, 0xbe, - 0x42, 0xb8, 0xbe, 0xf0, 0x38, 0xdc, 0xfd, 0x9c, 0xf6, 0x97, 0xe7, 0xf4, 0x6c, 0xe3, 0x66, 0xce, - 0x55, 0xaf, 0x19, 0xcf, 0xdf, 0x10, 0x3e, 0x68, 0x81, 0x90, 0xa3, 0xc2, 0x13, 0x3f, 0x80, 0xad, - 0x3d, 0xda, 0x71, 0xc9, 0xf0, 0x6a, 0x21, 0x64, 0x86, 0x1b, 0xef, 0xe1, 0x9d, 0x18, 0x04, 0xed, - 0xe9, 0x97, 0xba, 0x48, 0xa1, 0x05, 0xe2, 0xe2, 0x9c, 0xe4, 0x98, 0xfd, 0x07, 0xc2, 0x87, 0x8b, - 0x29, 0x6c, 0xa1, 0x3f, 0x2f, 0x96, 0xfb, 0xf3, 0x78, 0xe3, 0xfe, 0x2c, 0xea, 0x2e, 0xef, 0x90, - 0xd7, 0x9c, 0xdc, 0x58, 0x95, 0x97, 0x37, 0x56, 0xe5, 0xea, 0xc6, 0xaa, 0xfc, 0x3c, 0xb5, 0xd0, - 0x64, 0x6a, 0xa1, 0x97, 0x53, 0x0b, 0x5d, 0x4d, 0x2d, 0xf4, 0x6a, 0x6a, 0xa1, 0x5f, 0xfe, 0xb4, - 0x2a, 0xdf, 0x54, 0x47, 0x27, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x76, 0x7b, 0xeb, 0xac, 0x08, - 0x0b, 0x00, 0x00, + // 898 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0xcf, 0xa4, 0x69, 0x77, 0x3b, 0xed, 0xa6, 0xd5, 0x2c, 0x62, 0xad, 0x22, 0xb9, 0x91, 0x91, + 0x50, 0x10, 0xc2, 0xa6, 0x05, 0xc1, 0x9e, 0x40, 0x72, 0x5b, 0xed, 0x56, 0x2a, 0x21, 0x9a, 0xec, + 0x09, 0x71, 0xc0, 0x75, 0xde, 0x3a, 0xb3, 0x75, 0x6c, 0xe3, 0x99, 0x04, 0x72, 0x00, 0xf1, 0x0d, + 0x40, 0xe2, 0x0b, 0x71, 0xec, 0x71, 0x8f, 0x3d, 0xa0, 0x6a, 0x1b, 0x4e, 0xc0, 0x37, 0xe0, 0x84, + 0x66, 0x3c, 0x71, 0xec, 0xd6, 0xd1, 0xa2, 0xaa, 0x9b, 0x53, 0x3b, 0xf3, 0x7b, 0x7f, 0x7e, 0xef, + 0xfd, 0xe6, 0x3d, 0x07, 0x3f, 0x09, 0x98, 0x18, 0x8c, 0x4e, 0x6d, 0x3f, 0x1e, 0x3a, 0x71, 0x02, + 0x11, 0x1f, 0xb0, 0xe7, 0xc2, 0x89, 0x53, 0x16, 0xb0, 0xc8, 0x49, 0xce, 0x02, 0x27, 0x02, 0xf1, + 0x7d, 0x9c, 0x9e, 0x39, 0x5e, 0xc2, 0x78, 0x7e, 0x18, 0xef, 0x39, 0x01, 0x44, 0x90, 0x7a, 0x02, + 0xfa, 0x76, 0x92, 0xc6, 0x22, 0x26, 0x9f, 0xcd, 0x03, 0xd9, 0x79, 0x20, 0x3b, 0x0b, 0x64, 0x27, + 0x67, 0x81, 0xad, 0x7d, 0x6d, 0x19, 0x28, 0x3f, 0x8c, 0xf7, 0x76, 0x3e, 0x2c, 0x30, 0x08, 0xe2, + 0x20, 0x76, 0x54, 0xbc, 0xd3, 0xd1, 0x73, 0x75, 0x52, 0x07, 0xf5, 0x5f, 0x96, 0x67, 0xe7, 0x93, + 0xb3, 0xc7, 0xdc, 0x66, 0xb1, 0xe4, 0x33, 0xf4, 0xfc, 0x01, 0x8b, 0x20, 0x9d, 0x28, 0xa2, 0x8a, + 0xe0, 0x10, 0x84, 0x57, 0xc1, 0x6e, 0xc7, 0x59, 0xe4, 0x95, 0x8e, 0x22, 0xc1, 0x86, 0x70, 0xc3, + 0xe1, 0xd3, 0xd7, 0x39, 0x70, 0x7f, 0x00, 0x43, 0xef, 0x86, 0xdf, 0xc7, 0x8b, 0xfc, 0x46, 0x82, + 0x85, 0x0e, 0x8b, 0x04, 0x17, 0xe9, 0x75, 0x27, 0xeb, 0xaf, 0x15, 0xdc, 0x3c, 0x08, 0x47, 0x5c, + 0x40, 0xda, 0xc9, 0x1a, 0x43, 0xbe, 0xc5, 0xf7, 0x65, 0x2d, 0x7d, 0x4f, 0x78, 0x06, 0x6a, 0xa1, + 0xf6, 0xc6, 0xfe, 0x47, 0x76, 0x16, 0xda, 0x2e, 0x86, 0x56, 0x9d, 0x55, 0x1d, 0x95, 0xd6, 0xf6, + 0x78, 0xcf, 0xfe, 0xea, 0xf4, 0x05, 0xf8, 0xe2, 0x4b, 0x10, 0x9e, 0x4b, 0xce, 0x2f, 0x77, 0x6b, + 0xd3, 0xcb, 0x5d, 0x3c, 0xbf, 0xa3, 0x79, 0x54, 0xf2, 0x3e, 0xbe, 0xa7, 0x55, 0x30, 0xea, 0x2d, + 0xd4, 0x5e, 0x77, 0xb7, 0xb4, 0xf9, 0x3d, 0xcd, 0x81, 0xce, 0x70, 0x72, 0x88, 0xb7, 0x07, 0x31, + 0x17, 0x7c, 0x74, 0x1a, 0x81, 0x08, 0x21, 0x0a, 0xc4, 0xc0, 0x58, 0x69, 0xa1, 0xf6, 0x03, 0xd7, + 0xd0, 0x3e, 0xdb, 0x4f, 0x63, 0x2e, 0x7a, 0x0a, 0x3f, 0x51, 0x38, 0xbd, 0xe1, 0x41, 0x3e, 0xc7, + 0x4d, 0x0e, 0xe9, 0x98, 0xf9, 0xa0, 0x13, 0x18, 0x0d, 0x95, 0xf7, 0x6d, 0x1d, 0xa3, 0xd9, 0x2b, + 0xa1, 0xf4, 0x9a, 0x35, 0xd9, 0xc7, 0x38, 0x09, 0x47, 0x01, 0x8b, 0x3a, 0xde, 0x10, 0x8c, 0x55, + 0xe5, 0x9b, 0x97, 0xd8, 0xcd, 0x11, 0x5a, 0xb0, 0x22, 0xbf, 0x20, 0xbc, 0xe5, 0x97, 0x3a, 0xcb, + 0x8d, 0xb5, 0xd6, 0x4a, 0x7b, 0x63, 0xff, 0xc4, 0xbe, 0xe5, 0x83, 0xb5, 0xcb, 0x4a, 0x1d, 0x45, + 0x22, 0x9d, 0xb8, 0x8f, 0x34, 0x8f, 0xad, 0x32, 0xc8, 0xe9, 0xf5, 0xec, 0xd6, 0x8f, 0xf8, 0x61, + 0x45, 0x00, 0xd2, 0xc2, 0x0d, 0x9f, 0xf5, 0x53, 0xa5, 0xf5, 0xba, 0xbb, 0xa9, 0xc3, 0x35, 0x0e, + 0x8e, 0x0f, 0x29, 0x55, 0xc8, 0x4c, 0x84, 0x62, 0x93, 0x95, 0x70, 0xaf, 0x15, 0xa1, 0x78, 0x63, + 0xbd, 0x42, 0x98, 0x94, 0xf3, 0x9f, 0x30, 0x2e, 0xc8, 0x37, 0x37, 0x9e, 0x9b, 0xfd, 0xff, 0x9e, + 0x9b, 0xf4, 0x56, 0x8f, 0x6d, 0x5b, 0x93, 0xb8, 0x3f, 0xbb, 0x29, 0x3c, 0xb5, 0x10, 0xaf, 0x32, + 0x01, 0x43, 0x6e, 0xd4, 0x55, 0xeb, 0x9f, 0xdc, 0x51, 0xeb, 0xdd, 0x07, 0x3a, 0xe7, 0xea, 0xb1, + 0x8c, 0x4e, 0xb3, 0x24, 0xd6, 0x3f, 0x08, 0x3f, 0x3c, 0x0a, 0x52, 0xe0, 0x5c, 0xdb, 0x75, 0xe3, + 0x90, 0xf9, 0x93, 0x25, 0x8c, 0x54, 0x8a, 0x1b, 0x3c, 0x01, 0x5f, 0xc9, 0xb2, 0xb1, 0xdf, 0xbd, + 0x75, 0x99, 0x15, 0xec, 0x7b, 0x09, 0xf8, 0xf3, 0x67, 0x21, 0x4f, 0x54, 0xe5, 0xb2, 0xfe, 0x46, + 0xf8, 0x51, 0x85, 0xfd, 0x12, 0x54, 0xfd, 0xae, 0xac, 0xea, 0xc9, 0x5d, 0x96, 0xbb, 0x40, 0xda, + 0x9f, 0x2a, 0x6b, 0xed, 0x02, 0xa4, 0xe4, 0x31, 0xde, 0x94, 0x63, 0xd2, 0x83, 0x10, 0x7c, 0x11, + 0xcf, 0x06, 0xe9, 0x2d, 0x1d, 0x66, 0x53, 0x0e, 0xd2, 0x0c, 0xa3, 0x25, 0x4b, 0xb9, 0x08, 0xfb, + 0x11, 0x57, 0x4b, 0xe5, 0xda, 0x22, 0x3c, 0xec, 0xf4, 0xd4, 0x46, 0x99, 0xe1, 0xd6, 0x79, 0x75, + 0xb3, 0xe9, 0x28, 0x04, 0xf2, 0x05, 0x6e, 0x88, 0x49, 0x02, 0x3a, 0xf1, 0x07, 0x33, 0xa9, 0x9e, + 0x4d, 0x12, 0xf8, 0xf7, 0x72, 0xf7, 0x9d, 0x05, 0x6e, 0x12, 0xa6, 0xca, 0x91, 0x84, 0xb8, 0x2e, + 0xe2, 0x37, 0xf1, 0x76, 0x64, 0x7f, 0x5c, 0xac, 0x09, 0xd5, 0x9f, 0xc5, 0xb4, 0x2e, 0x62, 0xeb, + 0xb7, 0xea, 0x52, 0xe4, 0xcb, 0x22, 0x3f, 0xe0, 0x35, 0x50, 0x90, 0x81, 0x94, 0xb4, 0x77, 0xca, + 0x46, 0x56, 0xed, 0x36, 0x35, 0x9b, 0xb5, 0xcc, 0x80, 0xea, 0x7c, 0x72, 0x3d, 0xe1, 0xf9, 0x16, + 0x5b, 0xc2, 0xc8, 0xb6, 0x70, 0x43, 0xee, 0x48, 0xad, 0x7c, 0x3e, 0x60, 0x92, 0x03, 0x55, 0x08, + 0x79, 0x0f, 0xaf, 0xc9, 0xbf, 0xc7, 0x5d, 0xf5, 0xc9, 0x5b, 0x9f, 0x53, 0x7f, 0xaa, 0x6e, 0xa9, + 0x46, 0xa5, 0x5d, 0xf6, 0xb9, 0xd3, 0x9f, 0xb5, 0xdc, 0x2e, 0xab, 0x85, 0x6a, 0xd4, 0xba, 0x40, + 0xb8, 0x59, 0x58, 0xd4, 0x6f, 0x7e, 0x4e, 0x07, 0xe5, 0x39, 0x3d, 0xb8, 0xb5, 0x98, 0x73, 0xd6, + 0x0b, 0xc6, 0xf3, 0x77, 0x84, 0x37, 0x3b, 0x20, 0xe4, 0xa8, 0xf0, 0xc4, 0xf3, 0x61, 0x69, 0xbf, + 0x62, 0xa2, 0x8a, 0xe1, 0xd5, 0x44, 0xe8, 0x0c, 0x27, 0xef, 0xe2, 0xd5, 0x08, 0x04, 0xeb, 0xeb, + 0x9f, 0x2e, 0x79, 0x09, 0x1d, 0x10, 0xc7, 0x87, 0x34, 0xc3, 0xac, 0x3f, 0x10, 0xde, 0x2e, 0x96, + 0xb0, 0x04, 0x7d, 0x5e, 0x94, 0xf5, 0x39, 0xba, 0xb5, 0x3e, 0x45, 0xde, 0xd5, 0x0a, 0xb9, 0xed, + 0xf3, 0x2b, 0xb3, 0xf6, 0xf2, 0xca, 0xac, 0x5d, 0x5c, 0x99, 0xb5, 0x9f, 0xa7, 0x26, 0x3a, 0x9f, + 0x9a, 0xe8, 0xe5, 0xd4, 0x44, 0x17, 0x53, 0x13, 0xbd, 0x9a, 0x9a, 0xe8, 0xd7, 0x3f, 0xcd, 0xda, + 0xd7, 0xf5, 0xf1, 0xde, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x1a, 0xea, 0x2e, 0x19, 0x0c, + 0x00, 0x00, } diff --git a/pkg/network/apis/network/v1/generated.proto b/pkg/network/apis/network/v1/generated.proto index 6b08c1927c8b..ad7917a65290 100644 --- a/pkg/network/apis/network/v1/generated.proto +++ b/pkg/network/apis/network/v1/generated.proto @@ -31,6 +31,18 @@ message ClusterNetwork { // PluginName is the name of the network plugin being used optional string pluginName = 5; + + // ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from. + repeated ClusterNetworkEntry clusterNetworks = 6; +} + +// ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips. +message ClusterNetworkEntry { + // CIDR defines the total range of a cluster networks address space. + optional string cidr = 1; + + // HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods. + optional uint32 hostSubnetLength = 2; } // ClusterNetworkList is a collection of ClusterNetworks diff --git a/pkg/network/apis/network/v1/swagger_doc.go b/pkg/network/apis/network/v1/swagger_doc.go index 6b940fecc53e..15d4247f711d 100644 --- a/pkg/network/apis/network/v1/swagger_doc.go +++ b/pkg/network/apis/network/v1/swagger_doc.go @@ -12,12 +12,23 @@ var map_ClusterNetwork = map[string]string{ "hostsubnetlength": "HostSubnetLength is the number of bits of network to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods", "serviceNetwork": "ServiceNetwork is the CIDR range that Service IP addresses are allocated from", "pluginName": "PluginName is the name of the network plugin being used", + "clusterNetworks": "ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from.", } func (ClusterNetwork) SwaggerDoc() map[string]string { return map_ClusterNetwork } +var map_ClusterNetworkEntry = map[string]string{ + "": "ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips.", + "CIDR": "CIDR defines the total range of a cluster networks address space.", + "hostSubnetLength": "HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods.", +} + +func (ClusterNetworkEntry) SwaggerDoc() map[string]string { + return map_ClusterNetworkEntry +} + var map_ClusterNetworkList = map[string]string{ "": "ClusterNetworkList is a collection of ClusterNetworks", "metadata": "Standard object's metadata.", diff --git a/pkg/network/apis/network/v1/types.go b/pkg/network/apis/network/v1/types.go index 00683e07705f..95c77ff3d3de 100644 --- a/pkg/network/apis/network/v1/types.go +++ b/pkg/network/apis/network/v1/types.go @@ -21,17 +21,27 @@ type ClusterNetwork struct { metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` // Network is a CIDR string specifying the global overlay network's L3 space - Network string `json:"network" protobuf:"bytes,2,opt,name=network"` + Network string `json:"network,omitempty" protobuf:"bytes,2,opt,name=network"` // HostSubnetLength is the number of bits of network to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods - HostSubnetLength uint32 `json:"hostsubnetlength" protobuf:"varint,3,opt,name=hostsubnetlength"` + HostSubnetLength uint32 `json:"hostsubnetlength,omitempty" protobuf:"varint,3,opt,name=hostsubnetlength"` // ServiceNetwork is the CIDR range that Service IP addresses are allocated from ServiceNetwork string `json:"serviceNetwork" protobuf:"bytes,4,opt,name=serviceNetwork"` // PluginName is the name of the network plugin being used PluginName string `json:"pluginName,omitempty" protobuf:"bytes,5,opt,name=pluginName"` + // ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from. + ClusterNetworks []ClusterNetworkEntry `json:"clusterNetworks" protobuf:"bytes,6,rep,name=clusterNetworks"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips. +type ClusterNetworkEntry struct { + // CIDR defines the total range of a cluster networks address space. + CIDR string `json:"CIDR" protobuf:"bytes,1,opt,name=cidr"` + // HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods. + HostSubnetLength uint32 `json:"hostSubnetLength" protobuf:"varint,2,opt,name=hostSubnetLength"` +} + // ClusterNetworkList is a collection of ClusterNetworks type ClusterNetworkList struct { metav1.TypeMeta `json:",inline"` diff --git a/pkg/network/apis/network/v1/zz_generated.conversion.go b/pkg/network/apis/network/v1/zz_generated.conversion.go index bb33bbf392ee..8d361e463049 100644 --- a/pkg/network/apis/network/v1/zz_generated.conversion.go +++ b/pkg/network/apis/network/v1/zz_generated.conversion.go @@ -21,6 +21,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { return scheme.AddGeneratedConversionFuncs( Convert_v1_ClusterNetwork_To_network_ClusterNetwork, Convert_network_ClusterNetwork_To_v1_ClusterNetwork, + Convert_v1_ClusterNetworkEntry_To_network_ClusterNetworkEntry, + Convert_network_ClusterNetworkEntry_To_v1_ClusterNetworkEntry, Convert_v1_ClusterNetworkList_To_network_ClusterNetworkList, Convert_network_ClusterNetworkList_To_v1_ClusterNetworkList, Convert_v1_EgressNetworkPolicy_To_network_EgressNetworkPolicy, @@ -50,6 +52,7 @@ func autoConvert_v1_ClusterNetwork_To_network_ClusterNetwork(in *ClusterNetwork, out.HostSubnetLength = in.HostSubnetLength out.ServiceNetwork = in.ServiceNetwork out.PluginName = in.PluginName + out.ClusterNetworks = *(*[]network.ClusterNetworkEntry)(unsafe.Pointer(&in.ClusterNetworks)) return nil } @@ -60,6 +63,11 @@ func Convert_v1_ClusterNetwork_To_network_ClusterNetwork(in *ClusterNetwork, out func autoConvert_network_ClusterNetwork_To_v1_ClusterNetwork(in *network.ClusterNetwork, out *ClusterNetwork, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta + if in.ClusterNetworks == nil { + out.ClusterNetworks = make([]ClusterNetworkEntry, 0) + } else { + out.ClusterNetworks = *(*[]ClusterNetworkEntry)(unsafe.Pointer(&in.ClusterNetworks)) + } out.Network = in.Network out.HostSubnetLength = in.HostSubnetLength out.ServiceNetwork = in.ServiceNetwork @@ -72,9 +80,41 @@ func Convert_network_ClusterNetwork_To_v1_ClusterNetwork(in *network.ClusterNetw return autoConvert_network_ClusterNetwork_To_v1_ClusterNetwork(in, out, s) } +func autoConvert_v1_ClusterNetworkEntry_To_network_ClusterNetworkEntry(in *ClusterNetworkEntry, out *network.ClusterNetworkEntry, s conversion.Scope) error { + out.CIDR = in.CIDR + out.HostSubnetLength = in.HostSubnetLength + return nil +} + +// Convert_v1_ClusterNetworkEntry_To_network_ClusterNetworkEntry is an autogenerated conversion function. +func Convert_v1_ClusterNetworkEntry_To_network_ClusterNetworkEntry(in *ClusterNetworkEntry, out *network.ClusterNetworkEntry, s conversion.Scope) error { + return autoConvert_v1_ClusterNetworkEntry_To_network_ClusterNetworkEntry(in, out, s) +} + +func autoConvert_network_ClusterNetworkEntry_To_v1_ClusterNetworkEntry(in *network.ClusterNetworkEntry, out *ClusterNetworkEntry, s conversion.Scope) error { + out.CIDR = in.CIDR + out.HostSubnetLength = in.HostSubnetLength + return nil +} + +// Convert_network_ClusterNetworkEntry_To_v1_ClusterNetworkEntry is an autogenerated conversion function. +func Convert_network_ClusterNetworkEntry_To_v1_ClusterNetworkEntry(in *network.ClusterNetworkEntry, out *ClusterNetworkEntry, s conversion.Scope) error { + return autoConvert_network_ClusterNetworkEntry_To_v1_ClusterNetworkEntry(in, out, s) +} + func autoConvert_v1_ClusterNetworkList_To_network_ClusterNetworkList(in *ClusterNetworkList, out *network.ClusterNetworkList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]network.ClusterNetwork)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]network.ClusterNetwork, len(*in)) + for i := range *in { + if err := Convert_v1_ClusterNetwork_To_network_ClusterNetwork(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } @@ -85,10 +125,16 @@ func Convert_v1_ClusterNetworkList_To_network_ClusterNetworkList(in *ClusterNetw func autoConvert_network_ClusterNetworkList_To_v1_ClusterNetworkList(in *network.ClusterNetworkList, out *ClusterNetworkList, s conversion.Scope) error { out.ListMeta = in.ListMeta - if in.Items == nil { - out.Items = make([]ClusterNetwork, 0) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterNetwork, len(*in)) + for i := range *in { + if err := Convert_network_ClusterNetwork_To_v1_ClusterNetwork(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } } else { - out.Items = *(*[]ClusterNetwork)(unsafe.Pointer(&in.Items)) + out.Items = make([]ClusterNetwork, 0) } return nil } diff --git a/pkg/network/apis/network/v1/zz_generated.deepcopy.go b/pkg/network/apis/network/v1/zz_generated.deepcopy.go index 61dacdfccb74..6c4c5f1e0137 100644 --- a/pkg/network/apis/network/v1/zz_generated.deepcopy.go +++ b/pkg/network/apis/network/v1/zz_generated.deepcopy.go @@ -20,6 +20,7 @@ func init() { func RegisterDeepCopies(scheme *runtime.Scheme) error { return scheme.AddGeneratedDeepCopyFuncs( conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ClusterNetwork, InType: reflect.TypeOf(&ClusterNetwork{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ClusterNetworkEntry, InType: reflect.TypeOf(&ClusterNetworkEntry{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ClusterNetworkList, InType: reflect.TypeOf(&ClusterNetworkList{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_EgressNetworkPolicy, InType: reflect.TypeOf(&EgressNetworkPolicy{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_EgressNetworkPolicyList, InType: reflect.TypeOf(&EgressNetworkPolicyList{})}, @@ -44,6 +45,21 @@ func DeepCopy_v1_ClusterNetwork(in interface{}, out interface{}, c *conversion.C } else { out.ObjectMeta = *newVal.(*meta_v1.ObjectMeta) } + if in.ClusterNetworks != nil { + in, out := &in.ClusterNetworks, &out.ClusterNetworks + *out = make([]ClusterNetworkEntry, len(*in)) + copy(*out, *in) + } + return nil + } +} + +// DeepCopy_v1_ClusterNetworkEntry is an autogenerated deepcopy function. +func DeepCopy_v1_ClusterNetworkEntry(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*ClusterNetworkEntry) + out := out.(*ClusterNetworkEntry) + *out = *in return nil } } diff --git a/pkg/network/apis/network/validation/validation.go b/pkg/network/apis/network/validation/validation.go index 39c0583b7ee4..37bb93ce983d 100644 --- a/pkg/network/apis/network/validation/validation.go +++ b/pkg/network/apis/network/validation/validation.go @@ -3,12 +3,14 @@ package validation import ( "fmt" "net" + "reflect" "k8s.io/apimachinery/pkg/api/validation/path" utilvalidation "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/pkg/api/validation" + configapi "github.com/openshift/origin/pkg/cmd/server/api" "github.com/openshift/origin/pkg/network" networkapi "github.com/openshift/origin/pkg/network/apis/network" "github.com/openshift/origin/pkg/util/netutils" @@ -24,29 +26,53 @@ func SetDefaultClusterNetwork(cn networkapi.ClusterNetwork) { // ValidateClusterNetwork tests if required fields in the ClusterNetwork are set, and ensures that the "default" ClusterNetwork can only be set to the correct values func ValidateClusterNetwork(clusterNet *networkapi.ClusterNetwork) field.ErrorList { allErrs := validation.ValidateObjectMeta(&clusterNet.ObjectMeta, false, path.ValidatePathSegmentName, field.NewPath("metadata")) + var testedCIDRS []*net.IPNet - clusterIPNet, err := netutils.ParseCIDRMask(clusterNet.Network) - if err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, err.Error())) - } else { - maskLen, addrLen := clusterIPNet.Mask.Size() - if clusterNet.HostSubnetLength > uint32(addrLen-maskLen) { - allErrs = append(allErrs, field.Invalid(field.NewPath("hostSubnetLength"), clusterNet.HostSubnetLength, "subnet length is too large for clusterNetwork")) - } else if clusterNet.HostSubnetLength < 2 { - allErrs = append(allErrs, field.Invalid(field.NewPath("hostSubnetLength"), clusterNet.HostSubnetLength, "subnet length must be at least 2")) + if len(clusterNet.Network) != 0 || clusterNet.HostSubnetLength != 0 { + //In the case that a user manually makes a clusterNetwork object with clusterNet.Network and clusterNet.HostubnetLength at least make sure they are valid values + clusterIPNet, err := netutils.ParseCIDRMask(clusterNet.Network) + if err != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, err.Error())) + } else { + maskLen, addrLen := clusterIPNet.Mask.Size() + if clusterNet.HostSubnetLength > uint32(addrLen-maskLen) { + allErrs = append(allErrs, field.Invalid(field.NewPath("hostSubnetLength"), clusterNet.HostSubnetLength, "subnet length is too large for clusterNetwork")) + } else if clusterNet.HostSubnetLength < 2 { + allErrs = append(allErrs, field.Invalid(field.NewPath("hostSubnetLength"), clusterNet.HostSubnetLength, "subnet length must be at least 2")) + } } } + if len(clusterNet.ClusterNetworks) == 0 && len(clusterNet.Network) == 0 { + allErrs = append(allErrs, field.Invalid(field.NewPath("clusterNetworks"), clusterNet.ClusterNetworks, "must have at least one cluster network CIDR")) + } serviceIPNet, err := netutils.ParseCIDRMask(clusterNet.ServiceNetwork) if err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, err.Error())) } + for i, cn := range clusterNet.ClusterNetworks { + clusterIPNet, err := netutils.ParseCIDRMask(cn.CIDR) + if err != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("clusterNetworks").Index(i).Child("cidr"), cn.CIDR, err.Error())) + continue + } + maskLen, addrLen := clusterIPNet.Mask.Size() + if cn.HostSubnetLength > uint32(addrLen-maskLen) { + allErrs = append(allErrs, field.Invalid(field.NewPath("clusterNetworks").Index(i).Child("hostSubnetLength"), cn.HostSubnetLength, "subnet length is too large for clusterNetwork ")) + } else if cn.HostSubnetLength < 2 { + allErrs = append(allErrs, field.Invalid(field.NewPath("clusterNetworks").Index(i).Child("hostSubnetLength"), cn.HostSubnetLength, "subnet length must be at least 2")) + } - if (clusterIPNet != nil) && (serviceIPNet != nil) && clusterIPNet.Contains(serviceIPNet.IP) { - allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, "service network overlaps with cluster network")) - } - if (serviceIPNet != nil) && (clusterIPNet != nil) && serviceIPNet.Contains(clusterIPNet.IP) { - allErrs = append(allErrs, field.Invalid(field.NewPath("network"), clusterNet.Network, "cluster network overlaps with service network")) + for _, cidr := range testedCIDRS { + if configapi.CIDRsOverlap(clusterIPNet.String(), cidr.String()) { + allErrs = append(allErrs, field.Invalid(field.NewPath("clusterNetworks").Index(i).Child("cidr"), cn.CIDR, fmt.Sprintf("cidr range overlaps with another cidr %q", cidr.String()))) + } + } + testedCIDRS = append(testedCIDRS, clusterIPNet) + + if (clusterIPNet != nil) && (serviceIPNet != nil) && configapi.CIDRsOverlap(clusterIPNet.String(), serviceIPNet.String()) { + allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, fmt.Sprintf("service network overlaps with cluster network cidr: %s", clusterIPNet.String()))) + } } if clusterNet.Name == networkapi.ClusterNetworkDefault && defaultClusterNetwork != nil { @@ -56,6 +82,9 @@ func ValidateClusterNetwork(clusterNet *networkapi.ClusterNetwork) field.ErrorLi if clusterNet.HostSubnetLength != defaultClusterNetwork.HostSubnetLength { allErrs = append(allErrs, field.Invalid(field.NewPath("hostSubnetLength"), clusterNet.HostSubnetLength, "cannot change the default ClusterNetwork record via API.")) } + if !reflect.DeepEqual(clusterNet.ClusterNetworks, defaultClusterNetwork.ClusterNetworks) { + allErrs = append(allErrs, field.Invalid(field.NewPath("ClusterNetworks"), clusterNet.ClusterNetworks, "cannot change the default ClusterNetwork record via API")) + } if clusterNet.ServiceNetwork != defaultClusterNetwork.ServiceNetwork { allErrs = append(allErrs, field.Invalid(field.NewPath("serviceNetwork"), clusterNet.ServiceNetwork, "cannot change the default ClusterNetwork record via API.")) } diff --git a/pkg/network/apis/network/validation/validation_test.go b/pkg/network/apis/network/validation/validation_test.go index d58d7cae2b45..5cbf5c522d33 100644 --- a/pkg/network/apis/network/validation/validation_test.go +++ b/pkg/network/apis/network/validation/validation_test.go @@ -18,91 +18,139 @@ func TestValidateClusterNetwork(t *testing.T) { }{ { name: "Good one", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + }, + expectedErrors: 0, + }, + { + name: "Good one old network and hostsubnetlength set ", cn: &networkapi.ClusterNetwork{ ObjectMeta: metav1.ObjectMeta{Name: "any"}, Network: "10.20.0.0/16", HostSubnetLength: 8, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, ServiceNetwork: "172.30.0.0/16", }, expectedErrors: 0, }, { - name: "Bad network", + name: "only old network set ", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + Network: "10.20.0.0/16", + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + }, + expectedErrors: 1, + }, + { + name: "only old hostsubnetlength set ", cn: &networkapi.ClusterNetwork{ ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.0.0.0/16", HostSubnetLength: 8, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, ServiceNetwork: "172.30.0.0/16", }, expectedErrors: 1, }, + { + name: "Good one multiple addresses", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}, {CIDR: "10.128.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + }, + expectedErrors: 0, + }, + { + name: "Bad network", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + }, + expectedErrors: 1, + }, { name: "Bad network CIDR", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.0.1/16", - HostSubnetLength: 8, - ServiceNetwork: "172.30.0.0/16", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.1/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + }, + expectedErrors: 1, + }, + { + name: "Empty network ClusterNetworks", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ServiceNetwork: "172.30.0.0/16", }, expectedErrors: 1, }, { name: "Subnet length too large for network", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.30.0/24", - HostSubnetLength: 16, - ServiceNetwork: "172.30.0.0/16", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.30.0/24", HostSubnetLength: 16}}, + ServiceNetwork: "172.30.0.0/16", }, expectedErrors: 1, }, { name: "Subnet length too small", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.30.0/24", - HostSubnetLength: 1, - ServiceNetwork: "172.30.0.0/16", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 1}}, + ServiceNetwork: "172.30.0.0/16", }, expectedErrors: 1, }, { name: "Bad service network", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "1172.30.0.0/16", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "1172.30.0.0/16", }, expectedErrors: 1, }, { name: "Bad service network CIDR", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "172.30.1.0/16", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.1.0/16", }, expectedErrors: 1, }, { name: "Service network overlaps with cluster network", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "10.20.1.0/24", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "10.20.1.0/24", }, expectedErrors: 1, }, { name: "Cluster network overlaps with service network", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: "any"}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "10.0.0.0/8", + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "10.0.0.0/8", + }, + expectedErrors: 1, + }, + { + name: "Cluster networks overlap with each other", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: "any"}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.128.0.0/14", HostSubnetLength: 8}, {CIDR: "10.0.0.0/8", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", }, expectedErrors: 1, }, @@ -119,11 +167,10 @@ func TestValidateClusterNetwork(t *testing.T) { func TestSetDefaultClusterNetwork(t *testing.T) { defaultClusterNetwork := networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "172.30.0.0/16", - PluginName: "redhat/openshift-ovs-multitenant", + ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + PluginName: "redhat/openshift-ovs-multitenant", } SetDefaultClusterNetwork(defaultClusterNetwork) @@ -140,44 +187,50 @@ func TestSetDefaultClusterNetwork(t *testing.T) { { name: "Wrong Network", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, - Network: "10.30.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "172.30.0.0/16", - PluginName: "redhat/openshift-ovs-multitenant", + ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.30.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + PluginName: "redhat/openshift-ovs-multitenant", + }, + expectedErrors: 1, + }, + { + name: "Additional Network", + cn: &networkapi.ClusterNetwork{ + ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}, {CIDR: "10.30.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + PluginName: "redhat/openshift-ovs-multitenant", }, expectedErrors: 1, }, { name: "Wrong HostSubnetLength", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, - Network: "10.20.0.0/16", - HostSubnetLength: 9, - ServiceNetwork: "172.30.0.0/16", - PluginName: "redhat/openshift-ovs-multitenant", + ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 9}}, + ServiceNetwork: "172.30.0.0/16", + PluginName: "redhat/openshift-ovs-multitenant", }, expectedErrors: 1, }, { name: "Wrong ServiceNetwork", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "172.20.0.0/16", - PluginName: "redhat/openshift-ovs-multitenant", + ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.20.0.0/16", + PluginName: "redhat/openshift-ovs-multitenant", }, expectedErrors: 1, }, { name: "Wrong PluginName", cn: &networkapi.ClusterNetwork{ - ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, - Network: "10.20.0.0/16", - HostSubnetLength: 8, - ServiceNetwork: "172.30.0.0/16", - PluginName: "redhat/openshift-ovs-subnet", + ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.20.0.0/16", HostSubnetLength: 8}}, + ServiceNetwork: "172.30.0.0/16", + PluginName: "redhat/openshift-ovs-subnet", }, expectedErrors: 1, }, diff --git a/pkg/network/apis/network/zz_generated.deepcopy.go b/pkg/network/apis/network/zz_generated.deepcopy.go index 8ac3177fad68..4b75419b50b7 100644 --- a/pkg/network/apis/network/zz_generated.deepcopy.go +++ b/pkg/network/apis/network/zz_generated.deepcopy.go @@ -20,6 +20,7 @@ func init() { func RegisterDeepCopies(scheme *runtime.Scheme) error { return scheme.AddGeneratedDeepCopyFuncs( conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_network_ClusterNetwork, InType: reflect.TypeOf(&ClusterNetwork{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_network_ClusterNetworkEntry, InType: reflect.TypeOf(&ClusterNetworkEntry{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_network_ClusterNetworkList, InType: reflect.TypeOf(&ClusterNetworkList{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_network_EgressNetworkPolicy, InType: reflect.TypeOf(&EgressNetworkPolicy{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_network_EgressNetworkPolicyList, InType: reflect.TypeOf(&EgressNetworkPolicyList{})}, @@ -44,6 +45,21 @@ func DeepCopy_network_ClusterNetwork(in interface{}, out interface{}, c *convers } else { out.ObjectMeta = *newVal.(*v1.ObjectMeta) } + if in.ClusterNetworks != nil { + in, out := &in.ClusterNetworks, &out.ClusterNetworks + *out = make([]ClusterNetworkEntry, len(*in)) + copy(*out, *in) + } + return nil + } +} + +// DeepCopy_network_ClusterNetworkEntry is an autogenerated deepcopy function. +func DeepCopy_network_ClusterNetworkEntry(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*ClusterNetworkEntry) + out := out.(*ClusterNetworkEntry) + *out = *in return nil } } diff --git a/pkg/network/common/common.go b/pkg/network/common/common.go index 740f45c5d82d..d0cb5bde5736 100644 --- a/pkg/network/common/common.go +++ b/pkg/network/common/common.go @@ -11,6 +11,7 @@ import ( "github.com/golang/glog" + configapi "github.com/openshift/origin/pkg/cmd/server/api" networkapi "github.com/openshift/origin/pkg/network/apis/network" networkclient "github.com/openshift/origin/pkg/network/generated/internalclientset" "github.com/openshift/origin/pkg/util/netutils" @@ -33,20 +34,40 @@ func ClusterNetworkToString(n *networkapi.ClusterNetwork) string { return fmt.Sprintf("%s (network: %q, hostSubnetBits: %d, serviceNetwork: %q, pluginName: %q)", n.Name, n.Network, n.HostSubnetLength, n.ServiceNetwork, n.PluginName) } +func ClusterNetworkListContains(clusterNetworks []ClusterNetwork, ipaddr net.IP) (*net.IPNet, bool) { + for _, cn := range clusterNetworks { + if cn.ClusterCIDR.Contains(ipaddr) { + return cn.ClusterCIDR, true + } + } + return nil, false +} + type NetworkInfo struct { - ClusterNetwork *net.IPNet - ServiceNetwork *net.IPNet + ClusterNetworks []ClusterNetwork + ServiceNetwork *net.IPNet } -func ParseNetworkInfo(clusterNetwork string, serviceNetwork string) (*NetworkInfo, error) { - cn, err := netutils.ParseCIDRMask(clusterNetwork) - if err != nil { - _, cn, err := net.ParseCIDR(clusterNetwork) +type ClusterNetwork struct { + ClusterCIDR *net.IPNet + HostSubnetLength uint32 +} + +func ParseNetworkInfo(clusterNetwork []networkapi.ClusterNetworkEntry, serviceNetwork string) (*NetworkInfo, error) { + var cns []ClusterNetwork + + for _, entry := range clusterNetwork { + cidr, err := netutils.ParseCIDRMask(entry.CIDR) if err != nil { - return nil, fmt.Errorf("failed to parse ClusterNetwork CIDR %s: %v", clusterNetwork, err) + _, cidr, err := net.ParseCIDR(entry.CIDR) + if err != nil { + return nil, fmt.Errorf("failed to parse ClusterNetwork CIDR %s: %v", cidr, err) + } + glog.Errorf("Configured clusterNetworks value %q is invalid; treating it as %q", entry.CIDR, cidr.String()) } - glog.Errorf("Configured clusterNetworkCIDR value %q is invalid; treating it as %q", clusterNetwork, cn.String()) + cns = append(cns, ClusterNetwork{ClusterCIDR: cidr, HostSubnetLength: entry.HostSubnetLength}) } + sn, err := netutils.ParseCIDRMask(serviceNetwork) if err != nil { _, sn, err := net.ParseCIDR(serviceNetwork) @@ -57,8 +78,8 @@ func ParseNetworkInfo(clusterNetwork string, serviceNetwork string) (*NetworkInf } return &NetworkInfo{ - ClusterNetwork: cn, - ServiceNetwork: sn, + ClusterNetworks: cns, + ServiceNetwork: sn, }, nil } @@ -74,8 +95,8 @@ func (ni *NetworkInfo) ValidateNodeIP(nodeIP string) error { return fmt.Errorf("failed to parse node IP %s", nodeIP) } - if ni.ClusterNetwork.Contains(ipaddr) { - return fmt.Errorf("node IP %s conflicts with cluster network %s", nodeIP, ni.ClusterNetwork.String()) + if conflictingCIDR, found := ClusterNetworkListContains(ni.ClusterNetworks, ipaddr); found { + return fmt.Errorf("node IP %s conflicts with cluster network %s", nodeIP, conflictingCIDR.String()) } if ni.ServiceNetwork.Contains(ipaddr) { return fmt.Errorf("node IP %s conflicts with service network %s", nodeIP, ni.ServiceNetwork.String()) @@ -87,18 +108,14 @@ func (ni *NetworkInfo) ValidateNodeIP(nodeIP string) error { func (ni *NetworkInfo) CheckHostNetworks(hostIPNets []*net.IPNet) error { errList := []error{} for _, ipNet := range hostIPNets { - if ipNet.Contains(ni.ClusterNetwork.IP) { - errList = append(errList, fmt.Errorf("cluster IP: %s conflicts with host network: %s", ni.ClusterNetwork.IP.String(), ipNet.String())) - } - if ni.ClusterNetwork.Contains(ipNet.IP) { - errList = append(errList, fmt.Errorf("host network with IP: %s conflicts with cluster network: %s", ipNet.IP.String(), ni.ClusterNetwork.String())) + for _, clusterNetwork := range ni.ClusterNetworks { + if configapi.CIDRsOverlap(ipNet.String(), clusterNetwork.ClusterCIDR.String()) { + errList = append(errList, fmt.Errorf("cluster IP: %s conflicts with host network: %s", clusterNetwork.ClusterCIDR.IP.String(), ipNet.String())) + } } - if ipNet.Contains(ni.ServiceNetwork.IP) { + if configapi.CIDRsOverlap(ipNet.String(), ni.ServiceNetwork.String()) { errList = append(errList, fmt.Errorf("service IP: %s conflicts with host network: %s", ni.ServiceNetwork.String(), ipNet.String())) } - if ni.ServiceNetwork.Contains(ipNet.IP) { - errList = append(errList, fmt.Errorf("host network with IP: %s conflicts with service network: %s", ipNet.IP.String(), ni.ServiceNetwork.String())) - } } return kerrors.NewAggregate(errList) } @@ -110,8 +127,8 @@ func (ni *NetworkInfo) CheckClusterObjects(subnets []networkapi.HostSubnet, pods subnetIP, _, _ := net.ParseCIDR(subnet.Subnet) if subnetIP == nil { errList = append(errList, fmt.Errorf("failed to parse network address: %s", subnet.Subnet)) - } else if !ni.ClusterNetwork.Contains(subnetIP) { - errList = append(errList, fmt.Errorf("existing node subnet: %s is not part of cluster network: %s", subnet.Subnet, ni.ClusterNetwork.String())) + } else if _, contains := ClusterNetworkListContains(ni.ClusterNetworks, subnetIP); !contains { + errList = append(errList, fmt.Errorf("existing node subnet: %s is not part of any cluster network CIDR", subnet.Subnet)) } if len(errList) >= 10 { break @@ -121,8 +138,8 @@ func (ni *NetworkInfo) CheckClusterObjects(subnets []networkapi.HostSubnet, pods if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostNetwork { continue } - if pod.Status.PodIP != "" && !ni.ClusterNetwork.Contains(net.ParseIP(pod.Status.PodIP)) { - errList = append(errList, fmt.Errorf("existing pod %s:%s with IP %s is not part of cluster network %s", pod.Namespace, pod.Name, pod.Status.PodIP, ni.ClusterNetwork.String())) + if _, contains := ClusterNetworkListContains(ni.ClusterNetworks, net.ParseIP(pod.Status.PodIP)); !contains && pod.Status.PodIP != "" { + errList = append(errList, fmt.Errorf("existing pod %s:%s with IP %s is not part of cluster network", pod.Namespace, pod.Name, pod.Status.PodIP)) if len(errList) >= 10 { break } @@ -150,7 +167,7 @@ func GetNetworkInfo(networkClient networkclient.Interface) (*NetworkInfo, error) return nil, err } - return ParseNetworkInfo(cn.Network, cn.ServiceNetwork) + return ParseNetworkInfo(cn.ClusterNetworks, cn.ServiceNetwork) } type ResourceName string diff --git a/pkg/network/common/common_test.go b/pkg/network/common/common_test.go index 46df51acf6db..17e27393a3b7 100644 --- a/pkg/network/common/common_test.go +++ b/pkg/network/common/common_test.go @@ -35,7 +35,20 @@ func Test_checkHostNetworks(t *testing.T) { { name: "valid", networkInfo: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.128.0.0/14"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/14"), HostSubnetLength: 8}, + }, + ServiceNetwork: mustParseCIDR("172.30.0.0/16"), + }, + expectError: false, + }, + { + name: "valid multiple networks", + networkInfo: &NetworkInfo{ + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/14"), HostSubnetLength: 8}, + {ClusterCIDR: mustParseCIDR("15.128.0.0/14"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.30.0.0/16"), }, expectError: false, @@ -43,7 +56,9 @@ func Test_checkHostNetworks(t *testing.T) { { name: "hostIPNet inside ClusterNetwork", networkInfo: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.0.0.0/8"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.0.0.0/8"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.30.0.0/16"), }, expectError: true, @@ -51,7 +66,9 @@ func Test_checkHostNetworks(t *testing.T) { { name: "ClusterNetwork inside hostIPNet", networkInfo: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.1.0.0/16"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.1.0.0/16"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.30.0.0/16"), }, expectError: true, @@ -59,7 +76,9 @@ func Test_checkHostNetworks(t *testing.T) { { name: "hostIPNet inside ServiceNetwork", networkInfo: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.128.0.0/14"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/14"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.0.0.0/8"), }, expectError: true, @@ -67,7 +86,9 @@ func Test_checkHostNetworks(t *testing.T) { { name: "ServiceNetwork inside hostIPNet", networkInfo: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.128.0.0/14"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/14"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.20.30.0/8"), }, expectError: true, @@ -132,7 +153,9 @@ func Test_checkClusterObjects(t *testing.T) { { name: "valid", ni: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.128.0.0/14"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/14"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.30.0.0/16"), }, errs: []string{}, @@ -140,7 +163,9 @@ func Test_checkClusterObjects(t *testing.T) { { name: "Subnet 10.130.0.0/23 and Pod 10.130.0.10 outside of ClusterNetwork", ni: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.128.0.0/15"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/15"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.30.0.0/16"), }, errs: []string{"10.130.0.0/23", "10.130.0.10"}, @@ -148,7 +173,9 @@ func Test_checkClusterObjects(t *testing.T) { { name: "Service 172.30.99.99 outside of ServiceNetwork", ni: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("10.128.0.0/14"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("10.128.0.0/14"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("172.30.0.0/24"), }, errs: []string{"172.30.99.99"}, @@ -156,7 +183,9 @@ func Test_checkClusterObjects(t *testing.T) { { name: "Too-many-error truncation", ni: &NetworkInfo{ - ClusterNetwork: mustParseCIDR("1.2.3.0/24"), + ClusterNetworks: []ClusterNetwork{ + {ClusterCIDR: mustParseCIDR("1.2.3.0/24"), HostSubnetLength: 8}, + }, ServiceNetwork: mustParseCIDR("4.5.6.0/24"), }, errs: []string{"10.128.0.0/23", "10.129.0.0/23", "10.130.0.0/23", "10.128.0.2", "10.128.0.4", "10.128.0.6", "10.128.0.8", "10.129.0.3", "10.129.0.5", "10.129.0.7", "172.30.0.1", "too many errors"}, @@ -182,3 +211,49 @@ func Test_checkClusterObjects(t *testing.T) { } } } + +func Test_parseNetworkInfo(t *testing.T) { + tests := []struct { + name string + cidrs []networkapi.ClusterNetworkEntry + serviceNetwork string + err string + }{ + { + name: "valid single cidr", + cidrs: []networkapi.ClusterNetworkEntry{{CIDR: "10.0.0.0/16"}}, + serviceNetwork: "172.30.0.0/16", + err: "", + }, + { + name: "valid multiple cidr", + cidrs: []networkapi.ClusterNetworkEntry{{CIDR: "10.0.0.0/16"}, {CIDR: "10.4.0.0/16"}}, + serviceNetwork: "172.30.0.0/16", + err: "", + }, + { + name: "invalid CIDR address", + cidrs: []networkapi.ClusterNetworkEntry{{CIDR: "Invalid"}}, + serviceNetwork: "172.30.0.0/16", + err: "Invalid", + }, + { + name: "invalid serviceNetwork", + cidrs: []networkapi.ClusterNetworkEntry{{CIDR: "10.0.0.0/16"}}, + serviceNetwork: "172.30.0.0i/16", + err: "172.30.0.0i/16", + }, + } + for _, test := range tests { + _, err := ParseNetworkInfo(test.cidrs, test.serviceNetwork) + if err == nil { + if len(test.err) > 0 { + t.Fatalf("test %q unexpectedly did not get an error", test.name) + } + } else { + if !strings.Contains(err.Error(), test.err) { + t.Fatalf("test %q: error did not match %q: %v", test.name, test.err, err) + } + } + } +} diff --git a/pkg/network/master/master.go b/pkg/network/master/master.go index bc48e3468a15..3159e4bff951 100644 --- a/pkg/network/master/master.go +++ b/pkg/network/master/master.go @@ -4,7 +4,6 @@ package master import ( "fmt" - "net" "time" log "github.com/golang/glog" @@ -28,12 +27,12 @@ import ( ) type OsdnMaster struct { - kClient kclientset.Interface - networkClient networkclient.Interface - networkInfo *common.NetworkInfo - subnetAllocator *netutils.SubnetAllocator - vnids *masterVNIDMap - informers kinternalinformers.SharedInformerFactory + kClient kclientset.Interface + networkClient networkclient.Interface + networkInfo *common.NetworkInfo + subnetAllocatorList []*netutils.SubnetAllocator + vnids *masterVNIDMap + informers kinternalinformers.SharedInformerFactory // Holds Node IP used in creating host subnet for a node hostSubnetNodeIPs map[ktypes.UID]string @@ -54,7 +53,11 @@ func Start(networkConfig osconfigapi.MasterNetworkConfig, networkClient networkc } var err error - master.networkInfo, err = common.ParseNetworkInfo(networkConfig.ClusterNetworkCIDR, networkConfig.ServiceNetworkCIDR) + var clusterNetworkEntries []networkapi.ClusterNetworkEntry + for _, cidr := range networkConfig.ClusterNetworks { + clusterNetworkEntries = append(clusterNetworkEntries, networkapi.ClusterNetworkEntry{CIDR: cidr.CIDR, HostSubnetLength: cidr.HostSubnetLength}) + } + master.networkInfo, err = common.ParseNetworkInfo(clusterNetworkEntries, networkConfig.ServiceNetworkCIDR) if err != nil { return err } @@ -63,10 +66,9 @@ func Start(networkConfig osconfigapi.MasterNetworkConfig, networkClient networkc TypeMeta: metav1.TypeMeta{Kind: "ClusterNetwork"}, ObjectMeta: metav1.ObjectMeta{Name: networkapi.ClusterNetworkDefault}, - Network: networkConfig.ClusterNetworkCIDR, - HostSubnetLength: networkConfig.HostSubnetLength, - ServiceNetwork: networkConfig.ServiceNetworkCIDR, - PluginName: networkConfig.NetworkPluginName, + ClusterNetworks: clusterNetworkEntries, + ServiceNetwork: networkConfig.ServiceNetworkCIDR, + PluginName: networkConfig.NetworkPluginName, } osapivalidation.SetDefaultClusterNetwork(*configCN) @@ -102,6 +104,10 @@ func Start(networkConfig osconfigapi.MasterNetworkConfig, networkClient networkc if configChanged { configCN.TypeMeta = existingCN.TypeMeta configCN.ObjectMeta = existingCN.ObjectMeta + if err = master.checkClusterNetworkAgainstClusterObjects(); err != nil { + log.Errorf("ERROR: Attempting to modify cluster to excludes existing objects: %v", err) + return false, err + } if _, err = master.networkClient.Network().ClusterNetworks().Update(configCN); err != nil { return false, err } @@ -120,7 +126,7 @@ func Start(networkConfig osconfigapi.MasterNetworkConfig, networkClient networkc return err } - if err = master.SubnetStartMaster(master.networkInfo.ClusterNetwork, networkConfig.HostSubnetLength); err != nil { + if err = master.SubnetStartMaster(master.networkInfo.ClusterNetworks); err != nil { return err } @@ -166,38 +172,29 @@ func (master *OsdnMaster) checkClusterNetworkAgainstClusterObjects() error { } func clusterNetworkChanged(obj *networkapi.ClusterNetwork, old *networkapi.ClusterNetwork) (bool, error) { - changed := false - if old.Network != obj.Network { - changed = true - - _, newNet, err := net.ParseCIDR(obj.Network) - if err != nil { - return true, err - } - newSize, _ := newNet.Mask.Size() - oldBase, oldNet, err := net.ParseCIDR(old.Network) - if err != nil { - // Shouldn't happen, but if the existing value is invalid, then any change should be an improvement... - } else { - oldSize, _ := oldNet.Mask.Size() - - // oldSize and newSize are, eg the "16" in "10.1.0.0/16", so - // "newSize < oldSize" means the new network is larger - if !(newSize < oldSize && newNet.Contains(oldBase)) { - return true, fmt.Errorf("cannot change clusterNetworkCIDR to a value that does not include the existing network.") - } - } - } - if old.HostSubnetLength != obj.HostSubnetLength { - return true, fmt.Errorf("cannot change the hostSubnetLength of an already-deployed cluster") - } if old.ServiceNetwork != obj.ServiceNetwork { return true, fmt.Errorf("cannot change the serviceNetworkCIDR of an already-deployed cluster") - } - if old.PluginName != obj.PluginName { - changed = true - } + } else if old.PluginName != obj.PluginName { + return true, nil + } else if len(old.ClusterNetworks) != len(obj.ClusterNetworks) { + return true, nil + } else { + changed := false + for _, oldCIDR := range old.ClusterNetworks { + found := false + for _, newCIDR := range obj.ClusterNetworks { + if newCIDR.CIDR == oldCIDR.CIDR && newCIDR.HostSubnetLength == oldCIDR.HostSubnetLength { + found = true + break + } + } + if !found { + changed = true + break + } + } + return changed, nil - return changed, nil + } } diff --git a/pkg/network/master/master_test.go b/pkg/network/master/master_test.go index b1309449f40a..929f002c224f 100644 --- a/pkg/network/master/master_test.go +++ b/pkg/network/master/master_test.go @@ -11,10 +11,9 @@ import ( func Test_clusterNetworkChanged(t *testing.T) { origCN := networkapi.ClusterNetwork{ - Network: "10.128.0.0/14", - HostSubnetLength: 10, - ServiceNetwork: "172.30.0.0/16", - PluginName: "redhat/openshift-ovs-subnet", + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.128.0.0/14", HostSubnetLength: 10}}, + ServiceNetwork: "172.30.0.0/16", + PluginName: "redhat/openshift-ovs-subnet", } tests := []struct { @@ -30,45 +29,10 @@ func Test_clusterNetworkChanged(t *testing.T) { { name: "larger Network", changes: &networkapi.ClusterNetwork{ - Network: "10.128.0.0/12", + ClusterNetworks: []networkapi.ClusterNetworkEntry{{CIDR: "10.128.0.0/12"}}, }, expectError: false, }, - { - name: "larger Network", - changes: &networkapi.ClusterNetwork{ - Network: "10.0.0.0/8", - }, - expectError: false, - }, - { - name: "smaller Network", - changes: &networkapi.ClusterNetwork{ - Network: "10.128.0.0/15", - }, - expectError: true, - }, - { - name: "moved Network", - changes: &networkapi.ClusterNetwork{ - Network: "10.1.0.0/16", - }, - expectError: true, - }, - { - name: "larger HostSubnetLength", - changes: &networkapi.ClusterNetwork{ - HostSubnetLength: 11, - }, - expectError: true, - }, - { - name: "smaller HostSubnetLength", - changes: &networkapi.ClusterNetwork{ - HostSubnetLength: 9, - }, - expectError: true, - }, { name: "larger ServiceNetwork", changes: &networkapi.ClusterNetwork{ @@ -102,6 +66,10 @@ func Test_clusterNetworkChanged(t *testing.T) { for _, test := range tests { newCN := origCN expectChanged := false + if test.changes.ClusterNetworks != nil { + newCN.ClusterNetworks = test.changes.ClusterNetworks + expectChanged = true + } if test.changes.Network != "" { newCN.Network = test.changes.Network expectChanged = true diff --git a/pkg/network/master/subnets.go b/pkg/network/master/subnets.go index 882059fddd67..4dd2bef016cd 100644 --- a/pkg/network/master/subnets.go +++ b/pkg/network/master/subnets.go @@ -23,7 +23,7 @@ import ( "github.com/openshift/origin/pkg/util/netutils" ) -func (master *OsdnMaster) SubnetStartMaster(clusterNetwork *net.IPNet, hostSubnetLength uint32) error { +func (master *OsdnMaster) SubnetStartMaster(clusterNetworks []common.ClusterNetwork) error { subrange := make([]string, 0) subnets, err := master.networkClient.Network().HostSubnets().List(metav1.ListOptions{}) if err != nil { @@ -39,11 +39,15 @@ func (master *OsdnMaster) SubnetStartMaster(clusterNetwork *net.IPNet, hostSubne log.Infof("Found existing HostSubnet %s", common.HostSubnetToString(&sub)) } } - - master.subnetAllocator, err = netutils.NewSubnetAllocator(clusterNetwork.String(), hostSubnetLength, subrange) - if err != nil { - return err + var subnetAllocatorList []*netutils.SubnetAllocator + for _, cn := range clusterNetworks { + subnetAllocator, err := netutils.NewSubnetAllocator(cn.ClusterCIDR.String(), cn.HostSubnetLength, subrange) + if err != nil { + return err + } + subnetAllocatorList = append(subnetAllocatorList, subnetAllocator) } + master.subnetAllocatorList = subnetAllocatorList master.watchNodes() go utilwait.Forever(master.watchSubnets, 0) @@ -78,26 +82,33 @@ func (master *OsdnMaster) addNode(nodeName string, nodeIP string, hsAnnotations } } - // Create new subnet - sn, err := master.subnetAllocator.GetNetwork() - if err != nil { - return "", fmt.Errorf("error allocating network for node %s: %v", nodeName, err) - } - - sub = &networkapi.HostSubnet{ - TypeMeta: metav1.TypeMeta{Kind: "HostSubnet"}, - ObjectMeta: metav1.ObjectMeta{Name: nodeName, Annotations: hsAnnotations}, - Host: nodeName, - HostIP: nodeIP, - Subnet: sn.String(), - } - sub, err = master.networkClient.Network().HostSubnets().Create(sub) - if err != nil { - master.subnetAllocator.ReleaseNetwork(sn) - return "", fmt.Errorf("error creating subnet %s for node %s: %v", sn.String(), nodeName, err) + // Create new subet + for _, possibleSubnet := range master.subnetAllocatorList { + sn, err := possibleSubnet.GetNetwork() + if err == netutils.ErrSubnetAllocatorFull { + // Current subnet exhausted, check the next one + continue + } else if err != nil { + log.Errorf("Error allocating network from subnet: %v", possibleSubnet) + continue + } else { + sub = &networkapi.HostSubnet{ + TypeMeta: metav1.TypeMeta{Kind: "HostSubnet"}, + ObjectMeta: metav1.ObjectMeta{Name: nodeName, Annotations: hsAnnotations}, + Host: nodeName, + HostIP: nodeIP, + Subnet: sn.String(), + } + sub, err = master.networkClient.Network().HostSubnets().Create(sub) + if err != nil { + possibleSubnet.ReleaseNetwork(sn) + return "", fmt.Errorf("error allocating node: %s", nodeName) + } + log.Infof("Created HostSubnet %s", common.HostSubnetToString(sub)) + return nodeIP, nil + } } - log.Infof("Created HostSubnet %s", common.HostSubnetToString(sub)) - return nodeIP, nil + return "", fmt.Errorf("error allocating network for node %s: %v", nodeName, err) } func (master *OsdnMaster) deleteNode(nodeName string) error { @@ -270,7 +281,9 @@ func (master *OsdnMaster) watchSubnets() { if err != nil { return fmt.Errorf("error parsing subnet %q for node %q for deletion: %v", subnet, name, err) } - master.subnetAllocator.ReleaseNetwork(ipnet) + for _, possibleSubnetAllocator := range master.subnetAllocatorList { + possibleSubnetAllocator.ReleaseNetwork(ipnet) + } } } return nil diff --git a/pkg/network/node/iptables.go b/pkg/network/node/iptables.go index 46fed2bfd457..8a67a4586842 100644 --- a/pkg/network/node/iptables.go +++ b/pkg/network/node/iptables.go @@ -17,14 +17,14 @@ import ( type NodeIPTables struct { ipt iptables.Interface - clusterNetworkCIDR string + clusterNetworkCIDR []string syncPeriod time.Duration masqueradeServices bool mu sync.Mutex // Protects concurrent access to syncIPTableRules() } -func newNodeIPTables(clusterNetworkCIDR string, syncPeriod time.Duration, masqueradeServices bool) *NodeIPTables { +func newNodeIPTables(clusterNetworkCIDR []string, syncPeriod time.Duration, masqueradeServices bool) *NodeIPTables { return &NodeIPTables{ ipt: iptables.New(kexec.New(), utildbus.New(), iptables.ProtocolIpv4), clusterNetworkCIDR: clusterNetworkCIDR, @@ -105,13 +105,12 @@ func (n *NodeIPTables) syncIPTableRules() error { if err != nil { return fmt.Errorf("failed to ensure chain %s exists: %v", chain.name, err) } - // Create the rule pointing to it from its parent chain. Note that since we // use iptables.Prepend each time, chains with the same table and srcChain // (ie, OPENSHIFT-FIREWALL-FORWARD and OPENSHIFT-ADMIN-OUTPUT-RULES) will // run in *reverse* order of how they are listed in getNodeIPTablesChains(). _, err = n.ipt.EnsureRule(iptables.Prepend, iptables.Table(chain.table), iptables.Chain(chain.srcChain), append(chain.srcRule, "-j", chain.name)...) - if err != nil { + if err != nil && chain.name != "OPENSHIFT-MASQUERADE-2" { return fmt.Errorf("failed to ensure rule from %s to %s exists: %v", chain.srcChain, chain.name, err) } @@ -139,24 +138,11 @@ func (n *NodeIPTables) syncIPTableRules() error { const vxlanPort = "4789" func (n *NodeIPTables) getNodeIPTablesChains() []Chain { - var masqRule []string - if n.masqueradeServices { - masqRule = []string{"-s", n.clusterNetworkCIDR, "-m", "comment", "--comment", "masquerade pod-to-service and pod-to-external traffic", "-j", "MASQUERADE"} - } else { - masqRule = []string{"-s", n.clusterNetworkCIDR, "!", "-d", n.clusterNetworkCIDR, "-m", "comment", "--comment", "masquerade pod-to-external traffic", "-j", "MASQUERADE"} - } - return []Chain{ - { - table: "nat", - name: "OPENSHIFT-MASQUERADE", - srcChain: "POSTROUTING", - srcRule: []string{"-m", "comment", "--comment", "rules for masquerading OpenShift traffic"}, - rules: [][]string{ - masqRule, - }, - }, - { + var chainArray []Chain + + chainArray = append(chainArray, + Chain{ table: "filter", name: "OPENSHIFT-FIREWALL-ALLOW", srcChain: "INPUT", @@ -167,23 +153,54 @@ func (n *NodeIPTables) getNodeIPTablesChains() []Chain { {"-i", "docker0", "-m", "comment", "--comment", "from docker to localhost", "-j", "ACCEPT"}, }, }, - { - table: "filter", - name: "OPENSHIFT-FIREWALL-FORWARD", - srcChain: "FORWARD", - srcRule: []string{"-m", "comment", "--comment", "firewall overrides"}, - rules: [][]string{ - {"-s", n.clusterNetworkCIDR, "-m", "comment", "--comment", "attempted resend after connection close", "-m", "conntrack", "--ctstate", "INVALID", "-j", "DROP"}, - {"-d", n.clusterNetworkCIDR, "-m", "comment", "--comment", "forward traffic from SDN", "-j", "ACCEPT"}, - {"-s", n.clusterNetworkCIDR, "-m", "comment", "--comment", "forward traffic to SDN", "-j", "ACCEPT"}, - }, - }, - { + Chain{ table: "filter", name: "OPENSHIFT-ADMIN-OUTPUT-RULES", srcChain: "FORWARD", srcRule: []string{"-i", Tun0, "!", "-o", Tun0, "-m", "comment", "--comment", "administrator overrides"}, rules: nil, - }, + }) + + var masqRules [][]string + var masq2Rules [][]string + var filterRules [][]string + for _, cidr := range n.clusterNetworkCIDR { + if n.masqueradeServices { + masqRules = append(masqRules, []string{"-s", cidr, "-m", "comment", "--comment", "masquerade pod-to-service and pod-to-external traffic", "-j", "MASQUERADE"}) + } else { + masqRules = append(masqRules, []string{"-s", cidr, "-m", "comment", "--comment", "masquerade pod-to-external traffic", "-j", "OPENSHIFT-MASQUERADE-2"}) + masq2Rules = append(masq2Rules, []string{"-d", cidr, "-m", "comment", "--comment", "masquerade pod-to-external traffic", "-j", "RETURN"}) + } + + filterRules = append(filterRules, []string{"-s", cidr, "-m", "comment", "--comment", "attempted resend after connection close", "-m", "conntrack", "--ctstate", "INVALID", "-j", "DROP"}) + filterRules = append(filterRules, []string{"-d", cidr, "-m", "comment", "--comment", "forward traffic from SDN", "-j", "ACCEPT"}) + filterRules = append(filterRules, []string{"-s", cidr, "-m", "comment", "--comment", "forward traffic to SDN", "-j", "ACCEPT"}) } + + if !n.masqueradeServices { + masq2Rules = append(masq2Rules, []string{"-j", "MASQUERADE"}) + chainArray = append(chainArray, + Chain{ + table: "nat", + name: "OPENSHIFT-MASQUERADE-2", + rules: masq2Rules, + }) + } + + chainArray = append(chainArray, + Chain{ + table: "nat", + name: "OPENSHIFT-MASQUERADE", + srcChain: "POSTROUTING", + srcRule: []string{"-m", "comment", "--comment", "rules for masquerading OpenShift traffic"}, + rules: masqRules, + }, + Chain{ + table: "filter", + name: "OPENSHIFT-FIREWALL-FORWARD", + srcChain: "FORWARD", + srcRule: []string{"-m", "comment", "--comment", "firewall overrides"}, + rules: filterRules, + }) + return chainArray } diff --git a/pkg/network/node/networkpolicy.go b/pkg/network/node/networkpolicy.go index 2431ec1f0ff6..da109f7f2f1d 100644 --- a/pkg/network/node/networkpolicy.go +++ b/pkg/network/node/networkpolicy.go @@ -81,7 +81,9 @@ func (np *networkPolicyPlugin) Start(node *OsdnNode) error { } otx := node.oc.NewTransaction() - otx.AddFlow("table=21, priority=200, ip, nw_dst=%s, actions=ct(commit,table=30)", np.node.networkInfo.ClusterNetwork.String()) + for _, cn := range np.node.networkInfo.ClusterNetworks { + otx.AddFlow("table=21, priority=200, ip, nw_dst=%s, actions=ct(commit,table=30)", cn.ClusterCIDR.String()) + } otx.AddFlow("table=80, priority=200, ip, ct_state=+rpl, actions=output:NXM_NX_REG2[]") if err := otx.EndTransaction(); err != nil { return err diff --git a/pkg/network/node/node.go b/pkg/network/node/node.go index 84a3390596e0..41357ca0f6ea 100644 --- a/pkg/network/node/node.go +++ b/pkg/network/node/node.go @@ -311,7 +311,12 @@ func (node *OsdnNode) Start() error { return err } - nodeIPTables := newNodeIPTables(node.networkInfo.ClusterNetwork.String(), node.iptablesSyncPeriod, !node.useConnTrack) + var cidrList []string + for _, cn := range node.networkInfo.ClusterNetworks { + cidrList = append(cidrList, cn.ClusterCIDR.String()) + } + nodeIPTables := newNodeIPTables(cidrList, node.iptablesSyncPeriod, !node.useConnTrack) + if err = nodeIPTables.Setup(); err != nil { return fmt.Errorf("failed to set up iptables: %v", err) } @@ -334,7 +339,7 @@ func (node *OsdnNode) Start() error { } log.V(5).Infof("Starting openshift-sdn pod manager") - if err := node.podManager.Start(cniserver.CNIServerSocketPath, node.localSubnetCIDR, node.networkInfo.ClusterNetwork); err != nil { + if err := node.podManager.Start(cniserver.CNIServerSocketPath, node.localSubnetCIDR, node.networkInfo.ClusterNetworks); err != nil { return err } diff --git a/pkg/network/node/ovscontroller.go b/pkg/network/node/ovscontroller.go index e68931c1ab51..a0b6ff2f7729 100644 --- a/pkg/network/node/ovscontroller.go +++ b/pkg/network/node/ovscontroller.go @@ -63,7 +63,7 @@ func (oc *ovsController) AlreadySetUp() bool { return false } -func (oc *ovsController) SetupOVS(clusterNetworkCIDR, serviceNetworkCIDR, localSubnetCIDR, localSubnetGateway, nodeIP string) error { +func (oc *ovsController) SetupOVS(clusterNetworkCIDR []string, serviceNetworkCIDR, localSubnetCIDR, localSubnetGateway, nodeIP string) error { err := oc.ovs.AddBridge("fail-mode=secure", "protocols=OpenFlow13") if err != nil { return err @@ -84,22 +84,29 @@ func (oc *ovsController) SetupOVS(clusterNetworkCIDR, serviceNetworkCIDR, localS } otx := oc.ovs.NewTransaction() + // Table 0: initial dispatch based on in_port if oc.useConnTrack { otx.AddFlow("table=0, priority=300, ip, ct_state=-trk, actions=ct(table=0)") } // vxlan0 - otx.AddFlow("table=0, priority=200, in_port=1, arp, nw_src=%s, nw_dst=%s, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10", clusterNetworkCIDR, localSubnetCIDR) - otx.AddFlow("table=0, priority=200, in_port=1, ip, nw_src=%s, nw_dst=%s, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10", clusterNetworkCIDR, localSubnetCIDR) - otx.AddFlow("table=0, priority=200, in_port=1, ip, nw_src=%s, nw_dst=224.0.0.0/4, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10", clusterNetworkCIDR) + for _, clusterCIDR := range clusterNetworkCIDR { + otx.AddFlow("table=0, priority=200, in_port=1, arp, nw_src=%s, nw_dst=%s, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10", clusterCIDR, localSubnetCIDR) + otx.AddFlow("table=0, priority=200, in_port=1, ip, nw_src=%s, nw_dst=%s, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10", clusterCIDR, localSubnetCIDR) + otx.AddFlow("table=0, priority=200, in_port=1, ip, nw_src=%s, nw_dst=224.0.0.0/4, actions=move:NXM_NX_TUN_ID[0..31]->NXM_NX_REG0[],goto_table:10", clusterCIDR) + } otx.AddFlow("table=0, priority=150, in_port=1, actions=drop") // tun0 if oc.useConnTrack { otx.AddFlow("table=0, priority=400, in_port=2, ip, nw_src=%s, actions=goto_table:30", localSubnetGateway) - otx.AddFlow("table=0, priority=300, in_port=2, ip, nw_src=%s, nw_dst=%s, actions=goto_table:25", localSubnetCIDR, clusterNetworkCIDR) + for _, clusterCIDR := range clusterNetworkCIDR { + otx.AddFlow("table=0, priority=300, in_port=2, ip, nw_src=%s, nw_dst=%s, actions=goto_table:25", localSubnetCIDR, clusterCIDR) + } } otx.AddFlow("table=0, priority=250, in_port=2, ip, nw_dst=224.0.0.0/4, actions=drop") - otx.AddFlow("table=0, priority=200, in_port=2, arp, nw_src=%s, nw_dst=%s, actions=goto_table:30", localSubnetGateway, clusterNetworkCIDR) + for _, clusterCIDR := range clusterNetworkCIDR { + otx.AddFlow("table=0, priority=200, in_port=2, arp, nw_src=%s, nw_dst=%s, actions=goto_table:30", localSubnetGateway, clusterCIDR) + } otx.AddFlow("table=0, priority=200, in_port=2, ip, actions=goto_table:30") otx.AddFlow("table=0, priority=150, in_port=2, actions=drop") // else, from a container @@ -129,14 +136,18 @@ func (oc *ovsController) SetupOVS(clusterNetworkCIDR, serviceNetworkCIDR, localS // Table 30: general routing otx.AddFlow("table=30, priority=300, arp, nw_dst=%s, actions=output:2", localSubnetGateway) otx.AddFlow("table=30, priority=200, arp, nw_dst=%s, actions=goto_table:40", localSubnetCIDR) - otx.AddFlow("table=30, priority=100, arp, nw_dst=%s, actions=goto_table:50", clusterNetworkCIDR) + for _, clusterCIDR := range clusterNetworkCIDR { + otx.AddFlow("table=30, priority=100, arp, nw_dst=%s, actions=goto_table:50", clusterCIDR) + } otx.AddFlow("table=30, priority=300, ip, nw_dst=%s, actions=output:2", localSubnetGateway) otx.AddFlow("table=30, priority=100, ip, nw_dst=%s, actions=goto_table:60", serviceNetworkCIDR) if oc.useConnTrack { otx.AddFlow("table=30, priority=300, ip, nw_dst=%s, ct_state=+rpl, actions=ct(nat,table=70)", localSubnetCIDR) } otx.AddFlow("table=30, priority=200, ip, nw_dst=%s, actions=goto_table:70", localSubnetCIDR) - otx.AddFlow("table=30, priority=100, ip, nw_dst=%s, actions=goto_table:90", clusterNetworkCIDR) + for _, clusterCIDR := range clusterNetworkCIDR { + otx.AddFlow("table=30, priority=100, ip, nw_dst=%s, actions=goto_table:90", clusterCIDR) + } // Multicast coming from the VXLAN otx.AddFlow("table=30, priority=50, in_port=1, ip, nw_dst=224.0.0.0/4, actions=goto_table:120") diff --git a/pkg/network/node/ovscontroller_test.go b/pkg/network/node/ovscontroller_test.go index 60f6a1c78094..a8345ae66833 100644 --- a/pkg/network/node/ovscontroller_test.go +++ b/pkg/network/node/ovscontroller_test.go @@ -19,7 +19,7 @@ import ( func setup(t *testing.T) (ovs.Interface, *ovsController, []string) { ovsif := ovs.NewFake(Br0) oc := NewOVSController(ovsif, 0, true) - err := oc.SetupOVS("10.128.0.0/14", "172.30.0.0/16", "10.128.0.0/23", "10.128.0.1", "172.17.0.4") + err := oc.SetupOVS([]string{"10.128.0.0/14"}, "172.30.0.0/16", "10.128.0.0/23", "10.128.0.1", "172.17.0.4") if err != nil { t.Fatalf("Unexpected error setting up OVS: %v", err) } diff --git a/pkg/network/node/pod.go b/pkg/network/node/pod.go index 3894cac687a8..2dce9bca1f4f 100644 --- a/pkg/network/node/pod.go +++ b/pkg/network/node/pod.go @@ -11,6 +11,7 @@ import ( "syscall" networkapi "github.com/openshift/origin/pkg/network/apis/network" + "github.com/openshift/origin/pkg/network/common" "github.com/openshift/origin/pkg/network/node/cniserver" "github.com/openshift/origin/pkg/util/netutils" @@ -100,7 +101,7 @@ func newDefaultPodManager() *podManager { // Generates a CNI IPAM config from a given node cluster and local subnet that // CNI 'host-local' IPAM plugin will use to create an IP address lease for the // container -func getIPAMConfig(clusterNetwork *net.IPNet, localSubnet string) ([]byte, error) { +func getIPAMConfig(clusterNetworks []common.ClusterNetwork, localSubnet string) ([]byte, error) { nodeNet, err := cnitypes.ParseCIDR(localSubnet) if err != nil { return nil, fmt.Errorf("error parsing node network '%s': %v", localSubnet, err) @@ -120,6 +121,26 @@ func getIPAMConfig(clusterNetwork *net.IPNet, localSubnet string) ([]byte, error } _, mcnet, _ := net.ParseCIDR("224.0.0.0/4") + + routes := []cnitypes.Route{ + { + //Default route + Dst: net.IPNet{ + IP: net.IPv4zero, + Mask: net.IPMask(net.IPv4zero), + }, + GW: netutils.GenerateDefaultGateway(nodeNet), + }, + { + //Multicast + Dst: *mcnet, + }, + } + + for _, cn := range clusterNetworks { + routes = append(routes, cnitypes.Route{Dst: *cn.ClusterCIDR}) + } + return json.Marshal(&cniNetworkConfig{ // TODO: update to 0.3.0 spec CNIVersion: "0.2.0", @@ -131,36 +152,19 @@ func getIPAMConfig(clusterNetwork *net.IPNet, localSubnet string) ([]byte, error IP: nodeNet.IP, Mask: nodeNet.Mask, }, - Routes: []cnitypes.Route{ - { - // Default route - Dst: net.IPNet{ - IP: net.IPv4zero, - Mask: net.IPMask(net.IPv4zero), - }, - GW: netutils.GenerateDefaultGateway(nodeNet), - }, - { - // Cluster network - Dst: *clusterNetwork, - }, - { - // Multicast - Dst: *mcnet, - }, - }, + Routes: routes, }, }) } // Start the CNI server and start processing requests from it -func (m *podManager) Start(socketPath string, localSubnetCIDR string, clusterNetwork *net.IPNet) error { +func (m *podManager) Start(socketPath string, localSubnetCIDR string, clusterNetworks []common.ClusterNetwork) error { if m.enableHostports { m.hostportSyncer = kubehostport.NewHostportSyncer() } var err error - if m.ipamConfig, err = getIPAMConfig(clusterNetwork, localSubnetCIDR); err != nil { + if m.ipamConfig, err = getIPAMConfig(clusterNetworks, localSubnetCIDR); err != nil { return err } diff --git a/pkg/network/node/pod_test.go b/pkg/network/node/pod_test.go index 6df0df850bb1..ca2751484291 100644 --- a/pkg/network/node/pod_test.go +++ b/pkg/network/node/pod_test.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "github.com/openshift/origin/pkg/network/common" "github.com/openshift/origin/pkg/network/node/cniserver" utiltesting "k8s.io/client-go/util/testing" @@ -316,8 +317,8 @@ func TestPodManager(t *testing.T) { podTester := newPodTester(t, k, socketPath) podManager := newDefaultPodManager() podManager.podHandler = podTester - _, net, _ := net.ParseCIDR("1.2.0.0/16") - podManager.Start(socketPath, "1.2.3.0/24", net) + _, cidr, _ := net.ParseCIDR("1.2.0.0/16") + podManager.Start(socketPath, "1.2.3.0/24", []common.ClusterNetwork{{ClusterCIDR: cidr, HostSubnetLength: 8}}) // Add pods to our expected pod list before kicking off the // actual pod setup to ensure we don't concurrently access @@ -412,8 +413,8 @@ func TestDirectPodUpdate(t *testing.T) { podTester := newPodTester(t, "update", socketPath) podManager := newDefaultPodManager() podManager.podHandler = podTester - _, net, _ := net.ParseCIDR("1.2.0.0/16") - podManager.Start(socketPath, "1.2.3.0/24", net) + _, cidr, _ := net.ParseCIDR("1.2.0.0/16") + podManager.Start(socketPath, "1.2.3.0/24", []common.ClusterNetwork{{ClusterCIDR: cidr, HostSubnetLength: 8}}) op := &operation{ command: cniserver.CNI_UPDATE, diff --git a/pkg/network/node/sdn_controller.go b/pkg/network/node/sdn_controller.go index 0360d9d3fa7e..66e2beee3fd4 100644 --- a/pkg/network/node/sdn_controller.go +++ b/pkg/network/node/sdn_controller.go @@ -62,7 +62,7 @@ func (plugin *OsdnNode) getLocalSubnet() (string, error) { return subnet.Subnet, nil } -func (plugin *OsdnNode) alreadySetUp(localSubnetGatewayCIDR, clusterNetworkCIDR string) bool { +func (plugin *OsdnNode) alreadySetUp(localSubnetGatewayCIDR string, clusterNetworkCIDR []string) bool { var found bool l, err := netlink.LinkByName(Tun0) @@ -89,15 +89,17 @@ func (plugin *OsdnNode) alreadySetUp(localSubnetGatewayCIDR, clusterNetworkCIDR if err != nil { return false } - found = false for _, route := range routes { - if route.Dst != nil && route.Dst.String() == clusterNetworkCIDR { - found = true - break + found = false + for _, clusterCIDR := range clusterNetworkCIDR { + if route.Dst != nil && route.Dst.String() == clusterCIDR { + found = true + break + } + } + if !found { + return false } - } - if !found { - return false } if !plugin.oc.AlreadySetUp() { @@ -140,7 +142,11 @@ func deleteLocalSubnetRoute(device, localSubnetCIDR string) { } func (plugin *OsdnNode) SetupSDN() (bool, error) { - clusterNetworkCIDR := plugin.networkInfo.ClusterNetwork.String() + var clusterNetworkCIDRs []string + for _, cn := range plugin.networkInfo.ClusterNetworks { + clusterNetworkCIDRs = append(clusterNetworkCIDRs, cn.ClusterCIDR.String()) + } + serviceNetworkCIDR := plugin.networkInfo.ServiceNetwork.String() localSubnetCIDR := plugin.localSubnetCIDR @@ -161,13 +167,13 @@ func (plugin *OsdnNode) SetupSDN() (bool, error) { } gwCIDR := fmt.Sprintf("%s/%d", localSubnetGateway, localSubnetMaskLength) - if plugin.alreadySetUp(gwCIDR, clusterNetworkCIDR) { + if plugin.alreadySetUp(gwCIDR, clusterNetworkCIDRs) { glog.V(5).Infof("[SDN setup] no SDN setup required") return false, nil } glog.V(5).Infof("[SDN setup] full SDN setup required") - err = plugin.oc.SetupOVS(clusterNetworkCIDR, serviceNetworkCIDR, localSubnetCIDR, localSubnetGateway, plugin.localIP) + err = plugin.oc.SetupOVS(clusterNetworkCIDRs, serviceNetworkCIDR, localSubnetCIDR, localSubnetGateway, plugin.localIP) if err != nil { return false, err } @@ -187,12 +193,16 @@ func (plugin *OsdnNode) SetupSDN() (bool, error) { err = netlink.LinkSetUp(l) } if err == nil { - route := &netlink.Route{ - LinkIndex: l.Attrs().Index, - Scope: netlink.SCOPE_LINK, - Dst: plugin.networkInfo.ClusterNetwork, + for _, clusterNetwork := range plugin.networkInfo.ClusterNetworks { + route := &netlink.Route{ + LinkIndex: l.Attrs().Index, + Scope: netlink.SCOPE_LINK, + Dst: clusterNetwork.ClusterCIDR, + } + if err = netlink.RouteAdd(route); err != nil { + return false, err + } } - err = netlink.RouteAdd(route) } if err == nil { route := &netlink.Route{ diff --git a/pkg/network/proxy/proxy.go b/pkg/network/proxy/proxy.go index 2a46294e196c..c09d78706d75 100644 --- a/pkg/network/proxy/proxy.go +++ b/pkg/network/proxy/proxy.go @@ -266,7 +266,7 @@ func (proxy *OsdnProxy) endpointsBlocked(ep *kapi.Endpoints) bool { for _, ss := range ep.Subsets { for _, addr := range ss.Addresses { IP := net.ParseIP(addr.IP) - if !proxy.networkInfo.ClusterNetwork.Contains(IP) && !proxy.networkInfo.ServiceNetwork.Contains(IP) { + if _, contains := common.ClusterNetworkListContains(proxy.networkInfo.ClusterNetworks, IP); !contains && !proxy.networkInfo.ServiceNetwork.Contains(IP) { if proxy.firewallBlocksIP(ep.Namespace, IP) { glog.Warningf("Service '%s' in namespace '%s' has an Endpoint pointing to firewalled destination (%s)", ep.Name, ep.Namespace, addr.IP) return true diff --git a/pkg/oc/cli/describe/describer.go b/pkg/oc/cli/describe/describer.go index 4911b4e5bb7e..86357cc9b327 100644 --- a/pkg/oc/cli/describe/describer.go +++ b/pkg/oc/cli/describe/describer.go @@ -1689,10 +1689,14 @@ func (d *ClusterNetworkDescriber) Describe(namespace, name string, settings kpri } return tabbedString(func(out *tabwriter.Writer) error { formatMeta(out, cn.ObjectMeta) - formatString(out, "Cluster Network", cn.Network) - formatString(out, "Host Subnet Length", cn.HostSubnetLength) formatString(out, "Service Network", cn.ServiceNetwork) formatString(out, "Plugin Name", cn.PluginName) + fmt.Fprintf(out, "ClusterNetworks:\n") + fmt.Fprintf(out, "CIDR\tHost Subnet Length\n") + fmt.Fprintf(out, "----\t------------------\n") + for _, clusterNetwork := range cn.ClusterNetworks { + fmt.Fprintf(out, "%s\t%d\n", clusterNetwork.CIDR, clusterNetwork.HostSubnetLength) + } return nil }) } diff --git a/pkg/oc/cli/describe/printer.go b/pkg/oc/cli/describe/printer.go index 4710f7b35163..34fed4489682 100644 --- a/pkg/oc/cli/describe/printer.go +++ b/pkg/oc/cli/describe/printer.go @@ -62,7 +62,7 @@ var ( hostSubnetColumns = []string{"NAME", "HOST", "HOST IP", "SUBNET"} netNamespaceColumns = []string{"NAME", "NETID"} - clusterNetworkColumns = []string{"NAME", "NETWORK", "HOST SUBNET LENGTH", "SERVICE NETWORK", "PLUGIN NAME"} + clusterNetworkColumns = []string{"NAME", "CLUSTER NETWORKS", "SERVICE NETWORK", "PLUGIN NAME"} egressNetworkPolicyColumns = []string{"NAME"} clusterResourceQuotaColumns = []string{"NAME", "LABEL SELECTOR", "ANNOTATION SELECTOR"} @@ -1058,7 +1058,24 @@ func printNetNamespaceList(list *networkapi.NetNamespaceList, w io.Writer, opts func printClusterNetwork(n *networkapi.ClusterNetwork, w io.Writer, opts kprinters.PrintOptions) error { name := formatResourceName(opts.Kind, n.Name, opts.WithKind) - _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\n", name, n.Network, n.HostSubnetLength, n.ServiceNetwork, n.PluginName) + const numOfNetworksShown = 3 + var networksList []string + var networks string + for _, cidr := range n.ClusterNetworks { + networksList = append(networksList, fmt.Sprintf("%s:%d", cidr.CIDR, cidr.HostSubnetLength)) + } + + if _, err := fmt.Fprintf(w, "%s", name); err != nil { + return err + } + if len(networksList) > numOfNetworksShown { + networks = fmt.Sprintf("%s + %d more...", + strings.Join(networksList[:numOfNetworksShown], ", "), + len(networksList)-numOfNetworksShown) + } else { + networks = strings.Join(networksList, ", ") + } + _, err := fmt.Fprintf(w, "\t%s\t%s\t%s\n", networks, n.ServiceNetwork, n.PluginName) return err } diff --git a/pkg/openapi/zz_generated.openapi.go b/pkg/openapi/zz_generated.openapi.go index f0c3054132df..d8561dd4deac 100644 --- a/pkg/openapi/zz_generated.openapi.go +++ b/pkg/openapi/zz_generated.openapi.go @@ -6837,12 +6837,50 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope Format: "", }, }, + "clusterNetworks": { + SchemaProps: spec.SchemaProps{ + Description: "ClusterNetworks is a list of ClusterNetwork objects that defines the global overlay network's L3 space by specifying a set of CIDR and netmasks that the SDN can allocate addressed from.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/openshift/origin/pkg/network/apis/network/v1.ClusterNetworkEntry"), + }, + }, + }, + }, + }, }, - Required: []string{"network", "hostsubnetlength", "serviceNetwork"}, + Required: []string{"serviceNetwork", "clusterNetworks"}, }, }, Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/openshift/origin/pkg/network/apis/network/v1.ClusterNetworkEntry", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + }, + "github.com/openshift/origin/pkg/network/apis/network/v1.ClusterNetworkEntry": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterNetworkEntry defines an individual cluster network. The CIDRs cannot overlap with other cluster network CIDRs, CIDRs reserved for external ips, CIDRs reserved for service networks, and CIDRs reserved for ingress ips.", + Properties: map[string]spec.Schema{ + "CIDR": { + SchemaProps: spec.SchemaProps{ + Description: "CIDR defines the total range of a cluster networks address space.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostSubnetLength": { + SchemaProps: spec.SchemaProps{ + Description: "HostSubnetLength is the number of bits of the accompanying CIDR address to allocate to each node. eg, 8 would mean that each node would have a /24 slice of the overlay network for its pods.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"CIDR", "hostSubnetLength"}, + }, + }, + Dependencies: []string{}, }, "github.com/openshift/origin/pkg/network/apis/network/v1.ClusterNetworkList": { Schema: spec.Schema{ diff --git a/pkg/util/netutils/subnet_allocator.go b/pkg/util/netutils/subnet_allocator.go index ebe4258b235d..8d57035b2c9d 100644 --- a/pkg/util/netutils/subnet_allocator.go +++ b/pkg/util/netutils/subnet_allocator.go @@ -6,6 +6,8 @@ import ( "sync" ) +var ErrSubnetAllocatorFull = fmt.Errorf("No subnets available.") + type SubnetAllocator struct { network *net.IPNet hostBits uint32 @@ -112,7 +114,7 @@ func (sna *SubnetAllocator) GetNetwork() (*net.IPNet, error) { } sna.next = 0 - return nil, fmt.Errorf("No subnets available.") + return nil, ErrSubnetAllocatorFull } func (sna *SubnetAllocator) ReleaseNetwork(ipnet *net.IPNet) error { diff --git a/test/integration/endpoint_admission_test.go b/test/integration/endpoint_admission_test.go index 6fee80b0f3a9..375e4733d040 100644 --- a/test/integration/endpoint_admission_test.go +++ b/test/integration/endpoint_admission_test.go @@ -62,7 +62,12 @@ func TestEndpointAdmission(t *testing.T) { Configuration: &configapi.DefaultAdmissionConfig{}, }, } - masterConfig.NetworkConfig.ClusterNetworkCIDR = clusterNetworkCIDR + clusterNetworkConfig := []configapi.ClusterNetworkEntry{ + { + CIDR: clusterNetworkCIDR, + }, + } + masterConfig.NetworkConfig.ClusterNetworks = clusterNetworkConfig masterConfig.NetworkConfig.ServiceNetworkCIDR = serviceNetworkCIDR kubeConfigFile, err := testserver.StartConfiguredMaster(masterConfig) diff --git a/test/integration/etcd_storage_path_test.go b/test/integration/etcd_storage_path_test.go index b25a753934d6..1367afd16923 100644 --- a/test/integration/etcd_storage_path_test.go +++ b/test/integration/etcd_storage_path_test.go @@ -290,11 +290,11 @@ var etcdStorageData = map[schema.GroupVersionResource]struct { expectedGVK: gvkP("", "v1", "HostSubnet"), // expect the legacy group to be persisted }, gvr("", "v1", "clusternetworks"): { - stub: `{"metadata": {"name": "cn1"}, "network": "192.168.0.0/24", "hostsubnetlength": 4, "serviceNetwork": "192.168.1.0/24"}`, + stub: `{"metadata": {"name": "cn1"}, "serviceNetwork": "192.168.1.0/24", "clusterNetworks": [{"CIDR": "192.166.0.0/16", "hostSubnetLength": 8}]}`, expectedEtcdPath: "openshift.io/registry/sdnnetworks/cn1", }, gvr("network.openshift.io", "v1", "clusternetworks"): { - stub: `{"metadata": {"name": "cn1g"}, "network": "192.168.0.0/24", "hostsubnetlength": 4, "serviceNetwork": "192.168.1.0/24"}`, + stub: `{"metadata": {"name": "cn1g"}, "serviceNetwork": "192.168.1.0/24", "clusterNetworks": [{"CIDR": "192.167.0.0/16", "hostSubnetLength": 8}]}`, expectedEtcdPath: "openshift.io/registry/sdnnetworks/cn1g", expectedGVK: gvkP("", "v1", "ClusterNetwork"), // expect the legacy group to be persisted },