Boots servers on the availability zones

Added an availability zone scenario, which creates az's as
part of the context, boots specified number of VMs per az.
it has options for booting VMs on pre-existing az's also.

Co-authored by Asma Syed Hameed <asyedham@redhat.com>
Change-Id: I83ffe1d9b9a7001ecae7e28f0a91e1883a100f69

Change-Id: Ifdffc4aa464a7b45a31cf2e73748a2dfccbe7ea4
This commit is contained in:
Asma Syed Hameed 2023-07-17 17:13:21 +05:30 committed by rajeshP524
parent 173c9a1afa
commit daaa41c7d5
4 changed files with 204 additions and 0 deletions

View File

@ -407,6 +407,27 @@ workloads:
file: rally/octavia/octavia-create-delete-pools.yml
sla_max_failure: 0
- name: multi_scenario
enabled: false
type: rally
rally_deployment: overcloud
concurrency:
- 1
times: 1
scenarios:
- name: nova-boot-on-az
enabled: true
image_name: cirro5
flavor_name: m1.tiny-cirros
num_availability_zones: 3
# Any pre-created az's can be listed here. Rest of the az's will be
# created as part of the workload to match num_availability_zones
# Empty list is valid, in this case workload creates all the az's and use
# them for booting VMs
existing_availability_zones: []
num_vms_per_az: 3
file: rally/rally-plugins/nova/boot-servers-with-availability-zones.yml
- name: simple-plugins
enabled: false
type: rally

View File

@ -0,0 +1,40 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rally_openstack.common import consts
from rally_openstack.task.scenarios.nova import utils as nova_utils
from rally.task import scenario
from rally.task import types
from rally.task import validation
@types.convert(image={"type": "glance_image"}, flavor={"type": "nova_flavor"})
@validation.add("image_valid_on_flavor", flavor_param="flavor", image_param="image")
@validation.add("required_services", services=[consts.Service.NOVA])
@validation.add("required_platform", platform="openstack", users=True)
@validation.add("required_contexts", contexts=("create_availability_zones"))
@scenario.configure(context={"cleanup@openstack": ["nova"]},
name="BrowbeatPlugin.boot_servers_with_availability_zones",
platform="openstack")
class NovaBootWithAvailabilityZone(nova_utils.NovaScenario):
def run(self, image, flavor, num_vms_per_az, **kwargs):
"""Boot a server on the specified availability zones
:param image: image ID or instance for server creation
:param flavor: flavor ID or flavor name
:param num_vms_per_az: int, number of vm's per availability zone
:param kwargs: other optional parameters to initialize the server
"""
for availability_zone in self.context["availability_zones"]:
self._boot_servers(image, flavor, 1, num_vms_per_az,
availability_zone=availability_zone)

View File

@ -0,0 +1,43 @@
{% set image_name = image_name or 'cirros' %}
{% set flavor_name = flavor_name or 'm1.xtiny' %}
{% set num_vms_per_az = num_vms_per_az or 10 %}
{% set sla_max_avg_duration = sla_max_avg_duration or 60 %}
{% set sla_max_failure = sla_max_failure or 0 %}
{% set sla_max_seconds = sla_max_seconds or 60 %}
---
BrowbeatPlugin.boot_servers_with_availability_zones:
-
args:
flavor:
name: '{{ flavor_name }}'
image:
name: '{{ image_name }}'
num_vms_per_az: {{ num_vms_per_az }}
metadata:
test_metadata: "true"
runner:
concurrency: {{ concurrency }}
times: {{ times }}
type: 'constant'
context:
users:
tenants: 1
users_per_tenant: 8
quotas:
neutron:
network: -1
port: -1
router: -1
subnet: -1
nova:
instances: -1
cores: -1
ram: -1
create_availability_zones:
num_availability_zones: {{ num_availability_zones }}
requested_availability_zones: {{ existing_availability_zones }}
sla:
max_avg_duration: {{sla_max_avg_duration}}
max_seconds_per_iteration: {{sla_max_seconds}}
failure_rate:
max: {{sla_max_failure}}

View File

@ -0,0 +1,100 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rally import exceptions
from rally.task import context
from rally.common import logging
from rally import consts
from rally_openstack.common import osclients
LOG = logging.getLogger(__name__)
@context.configure(name="create_availability_zones", order=1000)
class CreateAvailabilityZonesContext(context.Context):
"""This plugin creates specified number of availability zones to boot servers."""
CONFIG_SCHEMA = {
"type": "object",
"$schema": consts.JSON_SCHEMA,
"additionalProperties": False,
"properties": {
"num_availability_zones": {
"type": "integer",
"minimum": 0
},
"requested_availability_zones":{
"type": "array",
"items": {
"type": "string"
},
"default": []
}
}
}
def get_nova_client(self):
return osclients.Clients(self.context["admin"]["credential"]).nova()
def setup(self):
"""This method is called before the task starts."""
num_availability_zones = self.config.get("num_availability_zones", 1)
requested_availability_zones = self.config.get("requested_availability_zones", [])
existing_availability_zones = self.get_nova_client().availability_zones.list(detailed=True)
self.available_hypervisors = []
self.context["availability_zones"] = list(requested_availability_zones)
for az in existing_availability_zones:
if az.zoneName == 'nova':
self.available_hypervisors = list(az.hosts.keys())
break
self.num_azs_to_be_created = (
num_availability_zones - len(requested_availability_zones)
)
if self.num_azs_to_be_created < 0:
err_msg = ("Invalid input passed. Make sure to pass correct"
" value for num_availability_zones")
raise exceptions.RallyException(err_msg)
if self.num_azs_to_be_created == 0:
return
if len(self.available_hypervisors)/self.num_azs_to_be_created < 1:
err_msg = ("Could not create availability zones as there are"
" no sufficient available hypervisors")
raise exceptions.RallyException(err_msg)
self.host_aggregates = []
for i in range(self.num_azs_to_be_created):
availability_zone = "crally_az" + str(i + 1)
aggregate_name = "crally_agg" + str(i + 1)
aggregate = self.get_nova_client().aggregates.create(aggregate_name, availability_zone)
self.host_aggregates.append(aggregate)
self.context["availability_zones"].append(availability_zone)
for counter, hypervisor in enumerate(self.available_hypervisors):
index = counter % self.num_azs_to_be_created
self.get_nova_client().aggregates.add_host(self.host_aggregates[index].id, hypervisor)
def cleanup(self):
"""This method is called after the task finishes."""
if self.num_azs_to_be_created <= 0:
return
for counter, hypervisor in enumerate(self.available_hypervisors):
index = counter % self.num_azs_to_be_created
self.get_nova_client().aggregates.remove_host(self.host_aggregates[index].id,
hypervisor)
for aggregate in self.host_aggregates:
self.get_nova_client().aggregates.delete(aggregate.id)