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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions data/data/install.openshift.io_installconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2492,6 +2492,99 @@ spec:
vCenter:
description: VCenter is the domain name or IP address of the vCenter.
type: string
vcenters:
description: VCenters slice is the configuration for the use of
Regions and Zones. Also deploying openshift cluster virtual
machines in multiple datacenters and clusters.
items:
description: VCenter stores the vCenter connection fields and
the Region slice that is used to create virtual machines in
various datacenters and clusters
properties:
password:
description: Password is the password for the user to use
to connect to the vCenter.
type: string
port:
description: Port is the vCenter API TCP port that the vCenter
SDK clients connect to.
type: integer
regions:
description: Regions slice is the configuration of the vCenter
datacenter and Zones that will be used in deployment of
virtual machines, cloud provider and tags.
items:
description: Region stores the name of the region, the
datacenter that defines the region and the Zones that
are a part of a particular region.
properties:
datacenter:
description: Datacenter is the name of the datacenter
to use in the vCenter.
type: string
name:
description: Name is the region name that will be
used for cloud provider and tag
type: string
zones:
description: Zones slice is the configuration of the
vCenter Resource Pool (optionally), Cluster, Network
and Datastore to deploy virtual machines.
items:
description: Zone stores the name of the zone, the
cluster associated with the zone. ResourcePool,
Network and Datastore is provided for virtual
machine deployment.
properties:
cluster:
description: Cluster is the name of the cluster
virtual machines will be cloned into.
type: string
datastore:
description: Datastore is the default datastore
to use for provisioning volumes.
type: string
name:
description: Name is the zone name that will
be used for the cloud provider and tag
type: string
network:
description: Network specifies the name of the
network to be used by the cluster.
type: string
resourcePool:
description: ResourcePool is the name of the
already configured Resource Pool to use when
deploying virtual machines.
type: string
required:
- cluster
- datastore
- name
- network
type: object
type: array
required:
- datacenter
- name
- zones
type: object
type: array
server:
description: Server is the domain name or IP address of
the vCenter.
type: string
user:
description: User is the name of the user to use to connect
to the vCenter.
type: string
required:
- password
- regions
- server
- user
type: object
type: array
required:
- datacenter
- defaultDatastore
Expand Down
44 changes: 44 additions & 0 deletions pkg/asset/installconfig/vsphere/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,46 @@ var (
validCIDR = "10.0.0.0/16"
)

func validIPIZoningInstallConfig() *types.InstallConfig {
return &types.InstallConfig{
Networking: &types.Networking{
MachineNetwork: []types.MachineNetworkEntry{
{CIDR: *ipnet.MustParseCIDR(validCIDR)},
},
},
Publish: types.ExternalPublishingStrategy,
Platform: types.Platform{
VSphere: &vsphere.Platform{
Cluster: "valid_cluster",
Datacenter: "valid_dc",
DefaultDatastore: "valid_ds",
Network: "valid_network",
Password: "valid_password",
Username: "valid_username",
VCenter: "valid-vcenter",
APIVIP: "192.168.111.0",
IngressVIP: "192.168.111.1",
VCenters: []vsphere.VCenter{{
Server: "valid-vcenter",
Password: "valid_password",
User: "valid_username",
Regions: []vsphere.Region{{
Name: "valid_region_name",
Datacenter: "valid_dc",
Zones: []vsphere.Zone{{
Name: "valid_zone_name",
Cluster: "valid_cluster",
Network: "valid_network",
ResourcePool: "valid_rp",
Datastore: "valid_ds",
}},
}},
}},
},
},
}
}

func validIPIInstallConfig() *types.InstallConfig {
return &types.InstallConfig{
Networking: &types.Networking{
Expand Down Expand Up @@ -52,6 +92,10 @@ func TestValidate(t *testing.T) {
name: "valid IPI install config",
installConfig: validIPIInstallConfig(),
validationMethod: validateProvisioning,
}, {
name: "valid IPI zoning install config",
installConfig: validIPIZoningInstallConfig(),
validationMethod: validateProvisioning,
}, {
name: "invalid IPI - no network",
installConfig: func() *types.InstallConfig {
Expand Down
73 changes: 73 additions & 0 deletions pkg/types/vsphere/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,77 @@ type Platform struct {
// specified, it will be set according to the default storage policy
// of vsphere.
DiskType DiskType `json:"diskType,omitempty"`

// VCenters slice is the configuration for the use of Regions and Zones. Also
// deploying openshift cluster virtual machines in multiple datacenters and clusters.
// +optional
VCenters []VCenter `json:"vcenters,omitempty"`
}

// VCenter stores the vCenter connection fields and the Region slice
// that is used to create virtual machines in various datacenters and clusters
type VCenter struct {
// Server is the domain name or IP address of the vCenter.
// +required
Server string `json:"server"`

// User is the name of the user to use to connect to the vCenter.
// +required
User string `json:"user"`

// Password is the password for the user to use to connect to the vCenter.
// +required
Password string `json:"password"`

// Port is the vCenter API TCP port that the vCenter SDK clients connect to.
// +optional
Port uint `json:"port,omitempty"`

// Regions slice is the configuration of the vCenter datacenter and Zones that
// will be used in deployment of virtual machines, cloud provider and tags.
// +required
Regions []Region `json:"regions"`
}

// Region stores the name of the region, the datacenter that defines the region and the Zones
// that are a part of a particular region.
type Region struct {

// Name is the region name that will be used for cloud provider and tag
// +required
Name string `json:"name"`

// Datacenter is the name of the datacenter to use in the vCenter.
// +required
Datacenter string `json:"datacenter"`

// Zones slice is the configuration of the vCenter Resource Pool (optionally),
// Cluster, Network and Datastore to deploy virtual machines.
// +required
Zones []Zone `json:"zones"`
}

// Zone stores the name of the zone, the cluster associated with the zone.
// ResourcePool, Network and Datastore is provided for virtual machine deployment.
type Zone struct {
// Name is the zone name that will be used for the cloud provider and tag
// +required
Name string `json:"name"`

// ResourcePool is the name of the already configured Resource Pool to use
// when deploying virtual machines.
// +optional
ResourcePool string `json:"resourcePool,omitempty"`

// Cluster is the name of the cluster virtual machines will be cloned into.
// +required
Cluster string `json:"cluster"`

// Network specifies the name of the network to be used by the cluster.
// +required
Network string `json:"network"`

// Datastore is the default datastore to use for provisioning volumes.
// +required
Datastore string `json:"datastore"`
}
73 changes: 73 additions & 0 deletions pkg/types/vsphere/validation/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ func ValidatePlatform(p *vsphere.Platform, fldPath *field.Path) field.ErrorList
}
}

// validate zoning
if len(p.VCenters) != 0 {
allErrs = append(allErrs, validateZoning(p, fldPath)...)
}

// If all VIPs are empty, skip IP validation. All VIPs are required to be defined together.
if strings.Join([]string{p.APIVIP, p.IngressVIP}, "") != "" {
allErrs = append(allErrs, validateVIPs(p, fldPath)...)
Expand Down Expand Up @@ -69,6 +74,74 @@ func ValidateForProvisioning(p *vsphere.Platform, fldPath *field.Path) field.Err
return allErrs
}

func validateZoning(p *vsphere.Platform, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

vCentersFieldPath := fldPath.Child("vcenters")
regionsFieldPath := vCentersFieldPath.Child("regions")
zonesFieldPath := regionsFieldPath.Child("zones")

// Check if the VCenters slice has entries
if len(p.VCenters) != 0 {
// We currently do not support more than one vCenter
if len(p.VCenters) > 1 {
allErrs = append(allErrs, field.Required(vCentersFieldPath, "must specify a single VCenters entry."))
}

for _, v := range p.VCenters {
if len(v.Server) != 0 {
if err := validate.Host(v.Server); err != nil {
allErrs = append(allErrs, field.Invalid(vCentersFieldPath.Child("server"), v.Server, "must be the domain name or IP address of the vCenter"))
}
}
if v.Port != 0 {
allErrs = append(allErrs, field.Required(vCentersFieldPath.Child("port"), "is currently not supported"))
}
if len(v.Server) == 0 {
allErrs = append(allErrs, field.Required(vCentersFieldPath.Child("server"), "must specify the name of the vCenter"))
}
if len(v.User) == 0 {
allErrs = append(allErrs, field.Required(vCentersFieldPath.Child("username"), "must specify the username"))
}
if len(v.Password) == 0 {
allErrs = append(allErrs, field.Required(vCentersFieldPath.Child("password"), "must specify the password"))
}
if len(v.Regions) == 0 {
allErrs = append(allErrs, field.Required(regionsFieldPath, "must specify the regions"))
} else {
for _, r := range v.Regions {
if len(r.Datacenter) == 0 {
allErrs = append(allErrs, field.Required(regionsFieldPath.Child("datacenter"), "must specify the datacenter"))
}
if len(r.Name) == 0 {
allErrs = append(allErrs, field.Required(regionsFieldPath.Child("name"), "must specify the region name"))
}
if len(r.Zones) == 0 {
allErrs = append(allErrs, field.Required(zonesFieldPath, "must specify the zones"))
} else {
for _, z := range r.Zones {
if len(z.Datastore) == 0 {
allErrs = append(allErrs, field.Required(zonesFieldPath.Child("datastore"), "must specify the datastore"))
}
if len(z.Network) == 0 {
allErrs = append(allErrs, field.Required(zonesFieldPath.Child("network"), "must specify a network"))
}
if len(z.Cluster) == 0 {
allErrs = append(allErrs, field.Required(zonesFieldPath.Child("cluster"), "must specify a cluster"))
}
if len(z.Name) == 0 {
allErrs = append(allErrs, field.Required(zonesFieldPath.Child("name"), "must specify a zone name"))
}
}
}
}
}
}
}

return allErrs
}

// validateVIPs checks that all required VIPs are provided and are valid IP addresses.
func validateVIPs(p *vsphere.Platform, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
Expand Down