Add default_availability_zone for VM creation

Add the LAUNCH_INSTANCE_DEFAULTS.default_availability_zone settings in
VM creation dashboard in order to let administrator select a default
availability zone instead of a random one when many are available.

Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
Change-Id: I297ff7f3c8e725c24c7f7687786c784f61f5d4e2
This commit is contained in:
David Hill 2020-09-09 13:38:43 -04:00 committed by Akihiro Motoki
parent 73f4469faa
commit fc8603a499
5 changed files with 107 additions and 5 deletions

View File

@ -2125,6 +2125,10 @@ LAUNCH_INSTANCE_DEFAULTS
Added the ``hide_create_volume`` option. Added the ``hide_create_volume`` option.
.. versionchanged:: 19.1.0(Wallaby)
Added the ``default_availability_zone`` option.
Default: Default:
.. code-block:: python .. code-block:: python
@ -2138,6 +2142,7 @@ Default:
"disable_volume": False, "disable_volume": False,
"disable_volume_snapshot": False, "disable_volume_snapshot": False,
"enable_scheduler_hints": True, "enable_scheduler_hints": True,
"default_availability_zone": "Any",
} }
A dictionary of settings which can be used to provide the default values for A dictionary of settings which can be used to provide the default values for
@ -2224,6 +2229,22 @@ Default: ``True``
This setting specifies whether or not Scheduler Hints can be provided when This setting specifies whether or not Scheduler Hints can be provided when
launching an instance. launching an instance.
default_availability_zone
#########################
.. versionadded:: 19.1.0(Wallaby)
Default: ``Any``
This setting allows an administrator to specify a default availability zone
for a new server creation. The valid value is ``Any`` or availability zone
list. If ``Any`` is specified, the default availability zone is decided by
the nova scheduler. If one of availability zones is specified, the specified
availability zone is used as the default availability zone. If a value
specified in this setting is not found in the availability zone list,
the setting will be ignored and the behavior will be same as when ``Any``
is specified.
LAUNCH_INSTANCE_LEGACY_ENABLED LAUNCH_INSTANCE_LEGACY_ENABLED
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -129,6 +129,7 @@
* cloud service properties, they should be READ-ONLY to all UI controllers * cloud service properties, they should be READ-ONLY to all UI controllers
*/ */
default_availability_zone: 'Any',
availabilityZones: [], availabilityZones: [],
flavors: [], flavors: [],
allowedBootSources: [], allowedBootSources: [],
@ -250,6 +251,7 @@
}); });
promise = $q.all([ promise = $q.all([
launchInstanceDefaults.then(setDefaultValues, noop),
novaAPI.getAvailabilityZones().then(onGetAvailabilityZones) novaAPI.getAvailabilityZones().then(onGetAvailabilityZones)
.finally(onGetAvailabilityZonesComplete), .finally(onGetAvailabilityZonesComplete),
novaAPI.getFlavors({ novaAPI.getFlavors({
@ -261,7 +263,6 @@
securityGroup.query().then(onGetSecurityGroups, noop), securityGroup.query().then(onGetSecurityGroups, noop),
serviceCatalog.ifTypeEnabled('network').then(getNetworks, noop), serviceCatalog.ifTypeEnabled('network').then(getNetworks, noop),
launchInstanceDefaults.then(addImageSourcesIfEnabled, noop), launchInstanceDefaults.then(addImageSourcesIfEnabled, noop),
launchInstanceDefaults.then(setDefaultValues, noop),
launchInstanceDefaults.then(addVolumeSourcesIfEnabled, noop) launchInstanceDefaults.then(addVolumeSourcesIfEnabled, noop)
]); ]);
@ -300,6 +301,9 @@
if ('hide_create_volume' in defaults) { if ('hide_create_volume' in defaults) {
model.newInstanceSpec.hide_create_volume = defaults.hide_create_volume; model.newInstanceSpec.hide_create_volume = defaults.hide_create_volume;
} }
if ('default_availability_zone' in defaults) {
model.default_availability_zone = defaults.default_availability_zone;
}
} }
/** /**
@ -365,13 +369,33 @@
if (model.availabilityZones.length === 1) { if (model.availabilityZones.length === 1) {
model.newInstanceSpec.availability_zone = model.availabilityZones[0].value; model.newInstanceSpec.availability_zone = model.availabilityZones[0].value;
} else if (model.availabilityZones.length > 1) { } else if (model.availabilityZones.length > 1) {
// There are 2 or more; allow ability for nova scheduler to pick, // There are 2 or more; allow ability for nova scheduler to pick any AZ
// and make that the default.
model.availabilityZones.unshift({ model.availabilityZones.unshift({
label: gettext("Any Availability Zone"), label: gettext("Any Availability Zone"),
value: "" value: ""
}); });
model.newInstanceSpec.availability_zone = model.availabilityZones[0].value; // if default_availability_zone is Any, pick the first one in the list
if (model.default_availability_zone === "Any") {
model.newInstanceSpec.availability_zone = model.availabilityZones[0].value;
} else {
var defaultZone = null;
for (var i = 0; i < model.availabilityZones.length; i++) {
if (model.availabilityZones[i].value === model.default_availability_zone) {
defaultZone = model.availabilityZones[i];
break;
}
}
if (defaultZone !== null) {
// if default_availability_zone is set, use that AZ by default.
model.newInstanceSpec.availability_zone = model.default_availability_zone;
// Add "(default)" suffix to the default AZ.
defaultZone.label = interpolate(gettext("%s (default)"), [defaultZone.value]);
} else {
// If the configured default AZ is not included in the AZ list (perhaps
// misconfiguration?), use the first one ("Any Availability Zone") by default.
model.newInstanceSpec.availability_zone = model.availabilityZones[0].value;
}
}
} }
} }

View File

@ -183,7 +183,8 @@
disable_image: false, disable_image: false,
disable_instance_snapshot: false, disable_instance_snapshot: false,
disable_volume: false, disable_volume: false,
disable_volume_snapshot: false disable_volume_snapshot: false,
default_availability_zone: 'Any'
}, },
DEFAULT_BOOT_SOURCE: 'image' DEFAULT_BOOT_SOURCE: 'image'
}; };
@ -505,6 +506,37 @@
expect(model.newInstanceSpec.create_volume_default).toBe(false); expect(model.newInstanceSpec.create_volume_default).toBe(false);
}); });
it('should default availability_zone to empty based on default', function() {
model.initialize(true);
scope.$apply();
expect(model.newInstanceSpec.availability_zone).toBe('');
});
it('should default availability_zone to empty based on setting', function() {
settings.LAUNCH_INSTANCE_DEFAULTS.default_availability_zone = 'Any';
model.initialize(true);
scope.$apply();
expect(model.newInstanceSpec.availability_zone).toBe('');
});
it('should default availability_zone based on setting', function() {
settings.LAUNCH_INSTANCE_DEFAULTS.default_availability_zone = 'zone-1';
model.initialize(true);
scope.$apply();
expect(model.newInstanceSpec.availability_zone).toEqual('zone-1');
});
it('should default AZ to empty if AZ configured is not found', function() {
settings.LAUNCH_INSTANCE_DEFAULTS.default_availability_zone = 'non-existing';
model.initialize(true);
scope.$apply();
expect(model.newInstanceSpec.availability_zone).toEqual('');
});
it('should default hide_create_volume to false if setting not provided', function() { it('should default hide_create_volume to false if setting not provided', function() {
delete settings.LAUNCH_INSTANCE_DEFAULTS.hide_create_volume; delete settings.LAUNCH_INSTANCE_DEFAULTS.hide_create_volume;
model.initialize(true); model.initialize(true);
@ -628,6 +660,22 @@
expect(model.allowedBootSources).toContain(INSTANCE_SNAPSHOT); expect(model.allowedBootSources).toContain(INSTANCE_SNAPSHOT);
}); });
it('should have proper availability_zone if specific setting is missing', function() {
delete settings.LAUNCH_INSTANCE_DEFAULTS.default_availability_zone;
model.initialize(true);
scope.$apply();
expect(model.newInstanceSpec.availability_zone).toBe('');
});
it('should have proper availability_zone if settings are missing', function() {
delete settings.LAUNCH_INSTANCE_DEFAULTS;
model.initialize(true);
scope.$apply();
expect(model.newInstanceSpec.availability_zone).toBe('');
});
it('should have proper allowedBootSources if settings are missing', function() { it('should have proper allowedBootSources if settings are missing', function() {
delete settings.LAUNCH_INSTANCE_DEFAULTS; delete settings.LAUNCH_INSTANCE_DEFAULTS;
model.initialize(true); model.initialize(true);

View File

@ -265,6 +265,7 @@ LAUNCH_INSTANCE_DEFAULTS = {
'disable_volume': False, 'disable_volume': False,
'disable_volume_snapshot': False, 'disable_volume_snapshot': False,
'enable_scheduler_hints': True, 'enable_scheduler_hints': True,
'default_availability_zone': 'Any',
} }
# The absolute path to the directory where message files are collected. # The absolute path to the directory where message files are collected.

View File

@ -0,0 +1,8 @@
---
features:
- |
When multiple availability zones are available, the default behavior is to
allow the scheduler to spawn a VM in any of them.
The new setting ``LAUNCH_INSTANCE_DEFAULTS.default_availability_zone``
allows an administrator to specify a default static availability zone
for new VM creation.