github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/vsphere/config.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package vsphere 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/schema" 9 "gopkg.in/juju/environschema.v1" 10 11 "github.com/juju/juju/environs/config" 12 "github.com/juju/juju/provider/vsphere/internal/vsphereclient" 13 ) 14 15 // The vmware-specific config keys. 16 const ( 17 cfgPrimaryNetwork = "primary-network" 18 cfgExternalNetwork = "external-network" 19 cfgDatastore = "datastore" 20 cfgForceVMHardwareVersion = "force-vm-hardware-version" 21 cfgEnableDiskUUID = "enable-disk-uuid" 22 cfgDiskProvisioningType = "disk-provisioning-type" 23 ) 24 25 // configFields is the spec for each vmware config value's type. 26 var ( 27 configSchema = environschema.Fields{ 28 cfgExternalNetwork: { 29 Description: "An external network that VMs will be connected to. The resulting IP address for a VM will be used as its public address.", 30 Type: environschema.Tstring, 31 }, 32 cfgDatastore: { 33 Description: "The datastore in which to create VMs. If this is not specified, the process will abort unless there is only one datastore available.", 34 Type: environschema.Tstring, 35 }, 36 cfgPrimaryNetwork: { 37 Description: "The primary network that VMs will be connected to. If this is not specified, Juju will look for a network named \"VM Network\".", 38 Type: environschema.Tstring, 39 }, 40 cfgForceVMHardwareVersion: { 41 Description: "The HW compatibility version to use when cloning a VM template to create a VM. The version must be supported by the remote compute resource, and greater or equal to the template’s version.", 42 Type: environschema.Tint, 43 }, 44 cfgEnableDiskUUID: { 45 Description: "Expose consistent disk UUIDs to the VM, equivalent to disk.EnableUUID. The default is True.", 46 Type: environschema.Tbool, 47 }, 48 cfgDiskProvisioningType: { 49 Description: "Specify how the disk should be provisioned when cloning the VM template. Allowed values are: thickEagerZero (default), thick and thin.", 50 Type: environschema.Tstring, 51 }, 52 } 53 54 configDefaults = schema.Defaults{ 55 cfgExternalNetwork: "", 56 cfgDatastore: schema.Omit, 57 cfgPrimaryNetwork: schema.Omit, 58 cfgForceVMHardwareVersion: int(0), 59 cfgEnableDiskUUID: true, 60 cfgDiskProvisioningType: string(vsphereclient.DiskTypeThick), 61 } 62 63 configRequiredFields = []string{} 64 configImmutableFields = []string{} 65 ) 66 67 type environConfig struct { 68 *config.Config 69 attrs map[string]interface{} 70 } 71 72 // newConfig builds a new environConfig from the provided Config and 73 // returns it. 74 func newConfig(cfg *config.Config) *environConfig { 75 return &environConfig{ 76 Config: cfg, 77 attrs: cfg.UnknownAttrs(), 78 } 79 } 80 81 var configFields = func() schema.Fields { 82 fs, _, err := configSchema.ValidationSchema() 83 if err != nil { 84 panic(err) 85 } 86 return fs 87 }() 88 89 // newValidConfig builds a new environConfig from the provided Config 90 // and returns it. The resulting config values are validated. 91 func newValidConfig(cfg *config.Config) (*environConfig, error) { 92 // Ensure that the provided config is valid. 93 if err := config.Validate(cfg, nil); err != nil { 94 return nil, errors.Trace(err) 95 } 96 97 // Apply the defaults and coerce/validate the custom config attrs. 98 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 99 if err != nil { 100 return nil, errors.Trace(err) 101 } 102 validCfg, err := cfg.Apply(validated) 103 if err != nil { 104 return nil, errors.Trace(err) 105 } 106 107 // Build the config. 108 ecfg := newConfig(validCfg) 109 110 // Do final validation. 111 if err := ecfg.validate(); err != nil { 112 return nil, errors.Trace(err) 113 } 114 115 return ecfg, nil 116 } 117 118 func (c *environConfig) externalNetwork() string { 119 return c.attrs[cfgExternalNetwork].(string) 120 } 121 122 func (c *environConfig) datastore() string { 123 ds, _ := c.attrs[cfgDatastore].(string) 124 return ds 125 } 126 127 func (c *environConfig) primaryNetwork() string { 128 network, _ := c.attrs[cfgPrimaryNetwork].(string) 129 return network 130 } 131 132 func (c *environConfig) enableDiskUUID() bool { 133 return c.attrs[cfgEnableDiskUUID].(bool) 134 } 135 136 func (c *environConfig) forceVMHardwareVersion() int64 { 137 versionVal := c.attrs[cfgForceVMHardwareVersion] 138 // It seems the value is properly cast to int when bootstrapping 139 // but it comes back as a float64 from the database, regardless of 140 // the checker used in configFields. 141 switch versionVal.(type) { 142 case float64: 143 v := c.attrs[cfgForceVMHardwareVersion].(float64) 144 return int64(v) 145 case int: 146 v := c.attrs[cfgForceVMHardwareVersion].(int) 147 return int64(v) 148 default: 149 return 0 150 } 151 } 152 153 func (c *environConfig) diskProvisioningType() vsphereclient.DiskProvisioningType { 154 provType, ok := c.attrs[cfgDiskProvisioningType] 155 if !ok { 156 // Return the default in case none is set. 157 return vsphereclient.DefaultDiskProvisioningType 158 } 159 160 provTypeStr, ok := provType.(string) 161 if !ok || provTypeStr == "" { 162 // We got an invalid value set, return default. 163 return vsphereclient.DefaultDiskProvisioningType 164 } 165 166 return vsphereclient.DiskProvisioningType(provTypeStr) 167 } 168 169 // Schema returns the configuration schema for an environment. 170 func (environProvider) Schema() environschema.Fields { 171 fields, err := config.Schema(configSchema) 172 if err != nil { 173 panic(err) 174 } 175 return fields 176 } 177 178 // ConfigSchema returns extra config attributes specific 179 // to this provider only. 180 func (p environProvider) ConfigSchema() schema.Fields { 181 return configFields 182 } 183 184 // ConfigDefaults returns the default values for the 185 // provider specific config attributes. 186 func (p environProvider) ConfigDefaults() schema.Defaults { 187 return configDefaults 188 } 189 190 // validate checks vmware-specific config values. 191 func (c environConfig) validate() error { 192 // All fields must be populated, even with just the default. 193 for _, field := range configRequiredFields { 194 if c.attrs[field].(string) == "" { 195 return errors.Errorf("%s: must not be empty", field) 196 } 197 } 198 199 if diskProvType, ok := c.attrs[cfgDiskProvisioningType]; ok { 200 diskProvTypeStr, ok := diskProvType.(string) 201 if !ok { 202 return errors.Errorf("%s must be a string", cfgDiskProvisioningType) 203 } 204 205 if diskProvTypeStr != "" { 206 found := false 207 for _, val := range vsphereclient.ValidDiskProvisioningTypes { 208 if vsphereclient.DiskProvisioningType(diskProvTypeStr) == val { 209 found = true 210 break 211 } 212 } 213 if !found { 214 return errors.Errorf( 215 "%q must be one of %q", cfgDiskProvisioningType, vsphereclient.ValidDiskProvisioningTypes) 216 } 217 } 218 } 219 return nil 220 } 221 222 // update applies changes from the provided config to the env config. 223 // Changes to any immutable attributes result in an error. 224 func (c *environConfig) update(cfg *config.Config) error { 225 // Validate the updates. newValidConfig does not modify the "known" 226 // config attributes so it is safe to call Validate here first. 227 if err := config.Validate(cfg, c.Config); err != nil { 228 return errors.Trace(err) 229 } 230 231 updates, err := newValidConfig(cfg) 232 if err != nil { 233 return errors.Trace(err) 234 } 235 236 // Check that no immutable fields have changed. 237 attrs := updates.UnknownAttrs() 238 for _, field := range configImmutableFields { 239 if attrs[field] != c.attrs[field] { 240 return errors.Errorf("%s: cannot change from %v to %v", field, c.attrs[field], attrs[field]) 241 } 242 } 243 244 // Apply the updates. 245 c.Config = cfg 246 c.attrs = cfg.UnknownAttrs() 247 return nil 248 }