From fc8603a499fb3302aeb2ebef714420e0fdcbdfef Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 9 Sep 2020 13:38:43 -0400 Subject: [PATCH] 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 Change-Id: I297ff7f3c8e725c24c7f7687786c784f61f5d4e2 --- doc/source/configuration/settings.rst | 21 ++++++++ .../launch-instance-model.service.js | 32 ++++++++++-- .../launch-instance-model.service.spec.js | 50 ++++++++++++++++++- openstack_dashboard/defaults.py | 1 + ...lt_availability_zone-9c070832b2992958.yaml | 8 +++ 5 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/add_default_availability_zone-9c070832b2992958.yaml diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index 935ba566e7..166c76db86 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst @@ -2125,6 +2125,10 @@ LAUNCH_INSTANCE_DEFAULTS Added the ``hide_create_volume`` option. +.. versionchanged:: 19.1.0(Wallaby) + + Added the ``default_availability_zone`` option. + Default: .. code-block:: python @@ -2138,6 +2142,7 @@ Default: "disable_volume": False, "disable_volume_snapshot": False, "enable_scheduler_hints": True, + "default_availability_zone": "Any", } 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 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js index 147b0a1204..dcec23d706 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js @@ -129,6 +129,7 @@ * cloud service properties, they should be READ-ONLY to all UI controllers */ + default_availability_zone: 'Any', availabilityZones: [], flavors: [], allowedBootSources: [], @@ -250,6 +251,7 @@ }); promise = $q.all([ + launchInstanceDefaults.then(setDefaultValues, noop), novaAPI.getAvailabilityZones().then(onGetAvailabilityZones) .finally(onGetAvailabilityZonesComplete), novaAPI.getFlavors({ @@ -261,7 +263,6 @@ securityGroup.query().then(onGetSecurityGroups, noop), serviceCatalog.ifTypeEnabled('network').then(getNetworks, noop), launchInstanceDefaults.then(addImageSourcesIfEnabled, noop), - launchInstanceDefaults.then(setDefaultValues, noop), launchInstanceDefaults.then(addVolumeSourcesIfEnabled, noop) ]); @@ -300,6 +301,9 @@ if ('hide_create_volume' in defaults) { 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) { model.newInstanceSpec.availability_zone = model.availabilityZones[0].value; } else if (model.availabilityZones.length > 1) { - // There are 2 or more; allow ability for nova scheduler to pick, - // and make that the default. + // There are 2 or more; allow ability for nova scheduler to pick any AZ model.availabilityZones.unshift({ label: gettext("Any Availability Zone"), 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; + } + } } } diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js index cc64988c91..e9d8d10f73 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js @@ -183,7 +183,8 @@ disable_image: false, disable_instance_snapshot: false, disable_volume: false, - disable_volume_snapshot: false + disable_volume_snapshot: false, + default_availability_zone: 'Any' }, DEFAULT_BOOT_SOURCE: 'image' }; @@ -505,6 +506,37 @@ 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() { delete settings.LAUNCH_INSTANCE_DEFAULTS.hide_create_volume; model.initialize(true); @@ -628,6 +660,22 @@ 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() { delete settings.LAUNCH_INSTANCE_DEFAULTS; model.initialize(true); diff --git a/openstack_dashboard/defaults.py b/openstack_dashboard/defaults.py index 45ce2d230a..693395b982 100644 --- a/openstack_dashboard/defaults.py +++ b/openstack_dashboard/defaults.py @@ -265,6 +265,7 @@ LAUNCH_INSTANCE_DEFAULTS = { 'disable_volume': False, 'disable_volume_snapshot': False, 'enable_scheduler_hints': True, + 'default_availability_zone': 'Any', } # The absolute path to the directory where message files are collected. diff --git a/releasenotes/notes/add_default_availability_zone-9c070832b2992958.yaml b/releasenotes/notes/add_default_availability_zone-9c070832b2992958.yaml new file mode 100644 index 0000000000..051a8f8d26 --- /dev/null +++ b/releasenotes/notes/add_default_availability_zone-9c070832b2992958.yaml @@ -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.