Merge "Reorganize Openstack context plugins"
This commit is contained in:
commit
2069982215
92
rally/plugins/openstack/context/ceilometer/samples.py
Normal file
92
rally/plugins/openstack/context/ceilometer/samples.py
Normal file
@ -0,0 +1,92 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally.plugins.openstack.scenarios.ceilometer import utils as ceilo_utils
|
||||
from rally.task import context
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="ceilometer", order=450)
|
||||
class CeilometerSampleGenerator(context.Context):
|
||||
"""Context for creating samples and collecting resources for benchmarks."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"counter_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"counter_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"counter_unit": {
|
||||
"type": "string"
|
||||
},
|
||||
"counter_volume": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"resources_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"samples_per_resource": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
},
|
||||
"required": ["counter_name", "counter_type", "counter_unit",
|
||||
"counter_volume"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"resources_per_tenant": 5,
|
||||
"samples_per_resource": 5
|
||||
}
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Ceilometer`"))
|
||||
def setup(self):
|
||||
counter_name = self.config["counter_name"]
|
||||
counter_type = self.config["counter_type"]
|
||||
counter_unit = self.config["counter_unit"]
|
||||
counter_volume = self.config["counter_volume"]
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
self.context["tenants"][tenant_id]["samples"] = []
|
||||
self.context["tenants"][tenant_id]["resources"] = []
|
||||
scenario = ceilo_utils.CeilometerScenario({"user": user})
|
||||
for i in range(self.config["resources_per_tenant"]):
|
||||
for j in range(self.config["samples_per_resource"]):
|
||||
sample = scenario._create_sample(counter_name,
|
||||
counter_type,
|
||||
counter_unit,
|
||||
counter_volume)
|
||||
self.context["tenants"][tenant_id]["samples"].append(
|
||||
sample[0].to_dict())
|
||||
|
||||
self.context["tenants"][tenant_id]["resources"].append(
|
||||
sample[0].resource_id)
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `Ceilometer`"))
|
||||
def cleanup(self):
|
||||
# We don't have API for removal of samples and resources
|
||||
pass
|
0
rally/plugins/openstack/context/cinder/__init__.py
Normal file
0
rally/plugins/openstack/context/cinder/__init__.py
Normal file
72
rally/plugins/openstack/context/cinder/volumes.py
Normal file
72
rally/plugins/openstack/context/cinder/volumes.py
Normal file
@ -0,0 +1,72 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.plugins.openstack.scenarios.cinder import utils as cinder_utils
|
||||
from rally.task import context
|
||||
from rally.task.scenarios import base as scenario_base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="volumes", order=420)
|
||||
class VolumeGenerator(context.Context):
|
||||
"""Context class for adding volumes to each user for benchmarks."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"volumes_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"required": ["size"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"volumes_per_tenant": 1
|
||||
}
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Volumes`"))
|
||||
def setup(self):
|
||||
size = self.config["size"]
|
||||
volumes_per_tenant = self.config["volumes_per_tenant"]
|
||||
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
self.context["tenants"][tenant_id].setdefault("volumes", [])
|
||||
cinder_util = cinder_utils.CinderScenario({"user": user})
|
||||
for i in range(volumes_per_tenant):
|
||||
rnd_name = scenario_base.Scenario._generate_random_name(
|
||||
prefix="ctx_rally_volume_")
|
||||
vol = cinder_util._create_volume(size, display_name=rnd_name)
|
||||
self.context["tenants"][tenant_id]["volumes"].append(vol._info)
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `Volumes`"))
|
||||
def cleanup(self):
|
||||
# TODO(boris-42): Delete only resources created by this context
|
||||
resource_manager.cleanup(names=["cinder.volumes"],
|
||||
users=self.context.get("users", []))
|
0
rally/plugins/openstack/context/glance/__init__.py
Normal file
0
rally/plugins/openstack/context/glance/__init__.py
Normal file
102
rally/plugins/openstack/context/glance/images.py
Normal file
102
rally/plugins/openstack/context/glance/images.py
Normal file
@ -0,0 +1,102 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.plugins.openstack.scenarios.glance import utils as glance_utils
|
||||
from rally.task import context
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="images", order=410)
|
||||
class ImageGenerator(context.Context):
|
||||
"""Context class for adding images to each user for benchmarks."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"image_url": {
|
||||
"type": "string",
|
||||
},
|
||||
"image_type": {
|
||||
"enum": ["qcow2", "raw", "vhd", "vmdk", "vdi", "iso", "aki",
|
||||
"ari", "ami"],
|
||||
},
|
||||
"image_container": {
|
||||
"type": "string",
|
||||
},
|
||||
"image_name": {
|
||||
"type": "string",
|
||||
},
|
||||
"min_ram": { # megabytes
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"min_disk": { # gigabytes
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"images_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
},
|
||||
"required": ["image_url", "image_type", "image_container",
|
||||
"images_per_tenant"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
def __init__(self, ctx):
|
||||
super(ImageGenerator, self).__init__(ctx)
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Images`"))
|
||||
def setup(self):
|
||||
image_url = self.config["image_url"]
|
||||
image_type = self.config["image_type"]
|
||||
image_container = self.config["image_container"]
|
||||
images_per_tenant = self.config["images_per_tenant"]
|
||||
image_name = self.config.get("image_name")
|
||||
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
current_images = []
|
||||
glance_scenario = glance_utils.GlanceScenario({"user": user})
|
||||
for i in range(images_per_tenant):
|
||||
if image_name and i > 0:
|
||||
cur_name = image_name + str(i)
|
||||
elif image_name:
|
||||
cur_name = image_name
|
||||
else:
|
||||
cur_name = None
|
||||
|
||||
image = glance_scenario._create_image(
|
||||
image_container, image_url, image_type,
|
||||
name=cur_name, prefix="rally_ctx_image_",
|
||||
min_ram=self.config.get("min_ram", 0),
|
||||
min_disk=self.config.get("min_disk", 0))
|
||||
current_images.append(image.id)
|
||||
|
||||
self.context["tenants"][tenant_id]["images"] = current_images
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `Images`"))
|
||||
def cleanup(self):
|
||||
# TODO(boris-42): Delete only resources created by this context
|
||||
resource_manager.cleanup(names=["glance.images"],
|
||||
users=self.context.get("users", []))
|
0
rally/plugins/openstack/context/heat/__init__.py
Normal file
0
rally/plugins/openstack/context/heat/__init__.py
Normal file
88
rally/plugins/openstack/context/heat/stacks.py
Normal file
88
rally/plugins/openstack/context/heat/stacks.py
Normal file
@ -0,0 +1,88 @@
|
||||
# Copyright 2015: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.plugins.openstack.scenarios.heat import utils as heat_utils
|
||||
from rally.task import context
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="stacks", order=435)
|
||||
class StackGenerator(context.Context):
|
||||
"""Context class for create temporary stacks with resources.
|
||||
|
||||
Stack generator allows to generate arbitrary number of stacks for
|
||||
each tenant before test scenarios. In addition, it allows to define
|
||||
number of resources (namely OS::Heat::RandomString) that will be created
|
||||
inside each stack. After test execution the stacks will be
|
||||
automatically removed from heat.
|
||||
"""
|
||||
|
||||
# The schema of the context configuration format
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
|
||||
"properties": {
|
||||
"stacks_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"resources_per_stack": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"stacks_per_tenant": 2,
|
||||
"resources_per_stack": 10
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _prepare_stack_template(res_num):
|
||||
template = {
|
||||
"heat_template_version": "2014-10-16",
|
||||
"description": "Test template for rally",
|
||||
"resources": {}
|
||||
}
|
||||
rand_string = {"type": "OS::Heat::RandomString"}
|
||||
for i in range(res_num):
|
||||
template["resources"]["TestResource%d" % i] = rand_string
|
||||
return template
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Stacks`"))
|
||||
def setup(self):
|
||||
template = self._prepare_stack_template(
|
||||
self.config["resources_per_stack"])
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
heat_scenario = heat_utils.HeatScenario({"user": user})
|
||||
self.context["tenants"][tenant_id]["stacks"] = []
|
||||
for i in range(self.config["stacks_per_tenant"]):
|
||||
stack = heat_scenario._create_stack(template)
|
||||
self.context["tenants"][tenant_id]["stacks"].append(stack.id)
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `Stacks`"))
|
||||
def cleanup(self):
|
||||
resource_manager.cleanup(names=["heat.stacks"],
|
||||
users=self.context.get("users", []))
|
94
rally/plugins/openstack/context/keystone/roles.py
Normal file
94
rally/plugins/openstack/context/keystone/roles.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally import exceptions
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.wrappers import keystone
|
||||
from rally.task import context
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="roles", order=330)
|
||||
class RoleGenerator(context.Context):
|
||||
"""Context class for adding temporary roles for benchmarks."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "array",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"items": {
|
||||
"type": "string",
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
def __init__(self, ctx):
|
||||
super(RoleGenerator, self).__init__(ctx)
|
||||
self.context["roles"] = []
|
||||
self.endpoint = self.context["admin"]["endpoint"]
|
||||
|
||||
def _add_role(self, admin_endpoint, context_role):
|
||||
"""Add role to users.
|
||||
|
||||
:param admin_endpoint: The base url.
|
||||
:param context_role: name of existing role.
|
||||
"""
|
||||
client = keystone.wrap(osclients.Clients(admin_endpoint).keystone())
|
||||
default_roles = client.list_roles()
|
||||
for def_role in default_roles:
|
||||
if str(def_role.name) == context_role:
|
||||
role = def_role
|
||||
break
|
||||
else:
|
||||
raise exceptions.NoSuchRole(role=context_role)
|
||||
|
||||
LOG.debug("Adding role %s to all users" % (role.id))
|
||||
for user in self.context["users"]:
|
||||
client.add_role(user_id=user["id"], role_id=role.id,
|
||||
project_id=user["tenant_id"])
|
||||
|
||||
return {"id": str(role.id), "name": str(role.name)}
|
||||
|
||||
def _remove_role(self, admin_endpoint, role):
|
||||
"""Remove given role from users.
|
||||
|
||||
:param admin_endpoint: The base url.
|
||||
:param role: dictionary with role parameters (id, name).
|
||||
"""
|
||||
client = keystone.wrap(osclients.Clients(admin_endpoint).keystone())
|
||||
|
||||
for user in self.context["users"]:
|
||||
with logging.ExceptionLogger(
|
||||
LOG, _("Failed to remove role: %s") % role["id"]):
|
||||
client.remove_role(
|
||||
user_id=user["id"], role_id=role["id"],
|
||||
project_id=user["tenant_id"])
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `roles`"))
|
||||
def setup(self):
|
||||
"""Add roles to all users."""
|
||||
for name in self.config:
|
||||
role = self._add_role(self.endpoint, name)
|
||||
self.context["roles"].append(role)
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `roles`"))
|
||||
def cleanup(self):
|
||||
"""Remove roles from users."""
|
||||
for role in self.context["roles"]:
|
||||
self._remove_role(self.endpoint, role)
|
282
rally/plugins/openstack/context/keystone/users.py
Normal file
282
rally/plugins/openstack/context/keystone/users.py
Normal file
@ -0,0 +1,282 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import collections
|
||||
import uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from rally.common import broker
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally import exceptions
|
||||
from rally import objects
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.wrappers import keystone
|
||||
from rally.plugins.openstack.wrappers import network
|
||||
from rally.task import context
|
||||
from rally.task import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
USER_CONTEXT_OPTS = [
|
||||
cfg.IntOpt("resource_management_workers",
|
||||
default=30,
|
||||
help="How many concurrent threads use for serving users "
|
||||
"context"),
|
||||
cfg.StrOpt("project_domain",
|
||||
default="default",
|
||||
help="ID of domain in which projects will be created."),
|
||||
cfg.StrOpt("user_domain",
|
||||
default="default",
|
||||
help="ID of domain in which users will be created."),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(USER_CONTEXT_OPTS,
|
||||
group=cfg.OptGroup(name="users_context",
|
||||
title="benchmark context options"))
|
||||
|
||||
|
||||
@context.context(name="users", order=100)
|
||||
class UserGenerator(context.Context):
|
||||
"""Context class for generating temporary users/tenants for benchmarks."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"tenants": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"users_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"resource_management_workers": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"project_domain": {
|
||||
"type": "string",
|
||||
},
|
||||
"user_domain": {
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
PATTERN_TENANT = "ctx_rally_%(task_id)s_tenant_%(iter)i"
|
||||
PATTERN_USER = "ctx_rally_%(tenant_id)s_user_%(uid)d"
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 1,
|
||||
"resource_management_workers":
|
||||
cfg.CONF.users_context.resource_management_workers,
|
||||
"project_domain": cfg.CONF.users_context.project_domain,
|
||||
"user_domain": cfg.CONF.users_context.user_domain
|
||||
}
|
||||
|
||||
def __init__(self, context):
|
||||
super(UserGenerator, self).__init__(context)
|
||||
self.context["users"] = []
|
||||
self.context["tenants"] = {}
|
||||
self.endpoint = self.context["admin"]["endpoint"]
|
||||
# NOTE(boris-42): I think this is the best place for adding logic when
|
||||
# we are using pre created users or temporary. So we
|
||||
# should rename this class s/UserGenerator/UserContext/
|
||||
# and change a bit logic of populating lists of users
|
||||
# and tenants
|
||||
|
||||
def _remove_default_security_group(self):
|
||||
"""Delete default security group for tenants."""
|
||||
clients = osclients.Clients(self.endpoint)
|
||||
|
||||
if consts.Service.NEUTRON not in clients.services().values():
|
||||
return
|
||||
|
||||
use_sg, msg = network.wrap(clients).supports_security_group()
|
||||
if not use_sg:
|
||||
LOG.debug("Security group context is disabled: %s" % msg)
|
||||
return
|
||||
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
with logging.ExceptionLogger(
|
||||
LOG, _("Unable to delete default security group")):
|
||||
uclients = osclients.Clients(user["endpoint"])
|
||||
sg = uclients.nova().security_groups.find(name="default")
|
||||
clients.neutron().delete_security_group(sg.id)
|
||||
|
||||
def _remove_associated_networks(self):
|
||||
"""Delete associated Nova networks from tenants."""
|
||||
# NOTE(rmk): Ugly hack to deal with the fact that Nova Network
|
||||
# networks can only be disassociated in an admin context. Discussed
|
||||
# with boris-42 before taking this approach [LP-Bug #1350517].
|
||||
clients = osclients.Clients(self.endpoint)
|
||||
if consts.Service.NOVA not in clients.services().values():
|
||||
return
|
||||
|
||||
nova_admin = clients.nova()
|
||||
|
||||
if not utils.check_service_status(nova_admin, "nova-network"):
|
||||
return
|
||||
|
||||
for net in nova_admin.networks.list():
|
||||
network_tenant_id = nova_admin.networks.get(net).project_id
|
||||
if network_tenant_id in self.context["tenants"]:
|
||||
try:
|
||||
nova_admin.networks.disassociate(net)
|
||||
except Exception as ex:
|
||||
LOG.warning("Failed disassociate net: %(tenant_id)s. "
|
||||
"Exception: %(ex)s" %
|
||||
{"tenant_id": network_tenant_id, "ex": ex})
|
||||
|
||||
def _create_tenants(self):
|
||||
threads = self.config["resource_management_workers"]
|
||||
|
||||
tenants = collections.deque()
|
||||
|
||||
def publish(queue):
|
||||
for i in range(self.config["tenants"]):
|
||||
args = (self.config["project_domain"], self.task["uuid"], i)
|
||||
queue.append(args)
|
||||
|
||||
def consume(cache, args):
|
||||
domain, task_id, i = args
|
||||
if "client" not in cache:
|
||||
clients = osclients.Clients(self.endpoint)
|
||||
cache["client"] = keystone.wrap(clients.keystone())
|
||||
tenant = cache["client"].create_project(
|
||||
self.PATTERN_TENANT % {"task_id": task_id, "iter": i}, domain)
|
||||
tenant_dict = {"id": tenant.id, "name": tenant.name}
|
||||
tenants.append(tenant_dict)
|
||||
|
||||
# NOTE(msdubov): consume() will fill the tenants list in the closure.
|
||||
broker.run(publish, consume, threads)
|
||||
tenants_dict = {}
|
||||
for t in tenants:
|
||||
tenants_dict[t["id"]] = t
|
||||
|
||||
return tenants_dict
|
||||
|
||||
def _create_users(self):
|
||||
# NOTE(msdubov): This should be called after _create_tenants().
|
||||
threads = self.config["resource_management_workers"]
|
||||
users_per_tenant = self.config["users_per_tenant"]
|
||||
|
||||
users = collections.deque()
|
||||
|
||||
def publish(queue):
|
||||
for tenant_id in self.context["tenants"]:
|
||||
for user_id in range(users_per_tenant):
|
||||
username = self.PATTERN_USER % {"tenant_id": tenant_id,
|
||||
"uid": user_id}
|
||||
password = str(uuid.uuid4())
|
||||
args = (username, password, self.config["project_domain"],
|
||||
self.config["user_domain"], tenant_id)
|
||||
queue.append(args)
|
||||
|
||||
def consume(cache, args):
|
||||
username, password, project_dom, user_dom, tenant_id = args
|
||||
if "client" not in cache:
|
||||
clients = osclients.Clients(self.endpoint)
|
||||
cache["client"] = keystone.wrap(clients.keystone())
|
||||
client = cache["client"]
|
||||
user = client.create_user(username, password,
|
||||
"%s@email.me" % username,
|
||||
tenant_id, user_dom)
|
||||
user_endpoint = objects.Endpoint(
|
||||
client.auth_url, user.name, password,
|
||||
self.context["tenants"][tenant_id]["name"],
|
||||
consts.EndpointPermission.USER, client.region_name,
|
||||
project_domain_name=project_dom, user_domain_name=user_dom,
|
||||
endpoint_type=self.endpoint.endpoint_type)
|
||||
users.append({"id": user.id,
|
||||
"endpoint": user_endpoint,
|
||||
"tenant_id": tenant_id})
|
||||
|
||||
# NOTE(msdubov): consume() will fill the users list in the closure.
|
||||
broker.run(publish, consume, threads)
|
||||
return list(users)
|
||||
|
||||
def _delete_tenants(self):
|
||||
threads = self.config["resource_management_workers"]
|
||||
|
||||
self._remove_associated_networks()
|
||||
|
||||
def publish(queue):
|
||||
for tenant_id in self.context["tenants"]:
|
||||
queue.append(tenant_id)
|
||||
|
||||
def consume(cache, tenant_id):
|
||||
if "client" not in cache:
|
||||
clients = osclients.Clients(self.endpoint)
|
||||
cache["client"] = keystone.wrap(clients.keystone())
|
||||
cache["client"].delete_project(tenant_id)
|
||||
|
||||
broker.run(publish, consume, threads)
|
||||
self.context["tenants"] = {}
|
||||
|
||||
def _delete_users(self):
|
||||
threads = self.config["resource_management_workers"]
|
||||
|
||||
def publish(queue):
|
||||
for user in self.context["users"]:
|
||||
queue.append(user["id"])
|
||||
|
||||
def consume(cache, user_id):
|
||||
if "client" not in cache:
|
||||
clients = osclients.Clients(self.endpoint)
|
||||
cache["client"] = keystone.wrap(clients.keystone())
|
||||
cache["client"].delete_user(user_id)
|
||||
|
||||
broker.run(publish, consume, threads)
|
||||
self.context["users"] = []
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `users`"))
|
||||
def setup(self):
|
||||
"""Create tenants and users, using the broker pattern."""
|
||||
threads = self.config["resource_management_workers"]
|
||||
|
||||
LOG.debug("Creating %(tenants)d tenants using %(threads)s threads" %
|
||||
{"tenants": self.config["tenants"], "threads": threads})
|
||||
self.context["tenants"] = self._create_tenants()
|
||||
|
||||
if len(self.context["tenants"]) < self.config["tenants"]:
|
||||
raise exceptions.ContextSetupFailure(
|
||||
ctx_name=self.get_name(),
|
||||
msg=_("Failed to create the requested number of tenants."))
|
||||
|
||||
users_num = self.config["users_per_tenant"] * self.config["tenants"]
|
||||
LOG.debug("Creating %(users)d users using %(threads)s threads" %
|
||||
{"users": users_num, "threads": threads})
|
||||
self.context["users"] = self._create_users()
|
||||
|
||||
if len(self.context["users"]) < users_num:
|
||||
raise exceptions.ContextSetupFailure(
|
||||
ctx_name=self.get_name(),
|
||||
msg=_("Failed to create the requested number of users."))
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `users`"))
|
||||
def cleanup(self):
|
||||
"""Delete tenants and users, using the broker pattern."""
|
||||
self._remove_default_security_group()
|
||||
self._delete_users()
|
||||
self._delete_tenants()
|
0
rally/plugins/openstack/context/murano/__init__.py
Normal file
0
rally/plugins/openstack/context/murano/__init__.py
Normal file
67
rally/plugins/openstack/context/murano/murano_packages.py
Normal file
67
rally/plugins/openstack/context/murano/murano_packages.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright 2015: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import zipfile
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common.i18n import _LE
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.task import context
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="murano_packages", order=401)
|
||||
class PackageGenerator(context.Context):
|
||||
"""Context class for uploading applications for murano."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"app_package": {
|
||||
"type": "string",
|
||||
}
|
||||
},
|
||||
"required": ["app_package"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Enter context: `Murano packages`"))
|
||||
def setup(self):
|
||||
if not zipfile.is_zipfile(self.config["app_package"]):
|
||||
msg = (_LE("There is no zip archive by this path: %s")
|
||||
% self.config["app_package"])
|
||||
raise OSError(msg)
|
||||
|
||||
for user, tenant_id in utils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
clients = osclients.Clients(user["endpoint"])
|
||||
self.context["tenants"][tenant_id]["packages"] = []
|
||||
package = clients.murano().packages.create(
|
||||
{"categories": ["Web"], "tags": ["tag"]},
|
||||
{"file": open(self.config["app_package"])})
|
||||
|
||||
self.context["tenants"][tenant_id]["packages"].append(package)
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Exit context: `Murano packages`"))
|
||||
def cleanup(self):
|
||||
resource_manager.cleanup(names=["murano.packages"],
|
||||
users=self.context.get("users", []))
|
0
rally/plugins/openstack/context/network/__init__.py
Normal file
0
rally/plugins/openstack/context/network/__init__.py
Normal file
120
rally/plugins/openstack/context/network/allow_ssh.py
Normal file
120
rally/plugins/openstack/context/network/allow_ssh.py
Normal file
@ -0,0 +1,120 @@
|
||||
# Copyright 2013: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.wrappers import network
|
||||
from rally.task import context
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SSH_GROUP_NAME = "rally_ssh_open"
|
||||
|
||||
|
||||
def _prepare_open_secgroup(endpoint, secgroup_name):
|
||||
"""Generate secgroup allowing all tcp/udp/icmp access.
|
||||
|
||||
In order to run tests on instances it is necessary to have SSH access.
|
||||
This function generates a secgroup which allows all tcp/udp/icmp access.
|
||||
|
||||
:param endpoint: clients endpoint
|
||||
:param secgroup_name: security group name
|
||||
|
||||
:returns: dict with security group details
|
||||
"""
|
||||
nova = osclients.Clients(endpoint).nova()
|
||||
|
||||
if secgroup_name not in [sg.name for sg in nova.security_groups.list()]:
|
||||
descr = "Allow ssh access to VMs created by Rally for benchmarking"
|
||||
rally_open = nova.security_groups.create(secgroup_name, descr)
|
||||
|
||||
rally_open = nova.security_groups.find(name=secgroup_name)
|
||||
|
||||
rules_to_add = [
|
||||
{
|
||||
"ip_protocol": "tcp",
|
||||
"to_port": 65535,
|
||||
"from_port": 1,
|
||||
"ip_range": {"cidr": "0.0.0.0/0"}
|
||||
},
|
||||
{
|
||||
"ip_protocol": "udp",
|
||||
"to_port": 65535,
|
||||
"from_port": 1,
|
||||
"ip_range": {"cidr": "0.0.0.0/0"}
|
||||
},
|
||||
{
|
||||
"ip_protocol": "icmp",
|
||||
"to_port": -1,
|
||||
"from_port": -1,
|
||||
"ip_range": {"cidr": "0.0.0.0/0"}
|
||||
}
|
||||
]
|
||||
|
||||
def rule_match(criteria, existing_rule):
|
||||
return all(existing_rule[key] == value
|
||||
for key, value in six.iteritems(criteria))
|
||||
|
||||
for new_rule in rules_to_add:
|
||||
if not any(rule_match(new_rule, existing_rule) for existing_rule
|
||||
in rally_open.rules):
|
||||
nova.security_group_rules.create(
|
||||
rally_open.id,
|
||||
from_port=new_rule["from_port"],
|
||||
to_port=new_rule["to_port"],
|
||||
ip_protocol=new_rule["ip_protocol"],
|
||||
cidr=new_rule["ip_range"]["cidr"])
|
||||
|
||||
return rally_open.to_dict()
|
||||
|
||||
|
||||
@context.context(name="allow_ssh", order=320)
|
||||
class AllowSSH(context.Context):
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Enter context: `allow_ssh`"))
|
||||
def setup(self):
|
||||
admin_or_user = (self.context.get("admin") or
|
||||
self.context.get("users")[0])
|
||||
|
||||
net_wrapper = network.wrap(
|
||||
osclients.Clients(admin_or_user["endpoint"]),
|
||||
self.config)
|
||||
use_sg, msg = net_wrapper.supports_security_group()
|
||||
if not use_sg:
|
||||
LOG.info(_("Security group context is disabled: %s") % msg)
|
||||
return
|
||||
|
||||
secgroup_name = "%s_%s" % (SSH_GROUP_NAME,
|
||||
self.context["task"]["uuid"])
|
||||
|
||||
for user in self.context["users"]:
|
||||
user["secgroup"] = _prepare_open_secgroup(user["endpoint"],
|
||||
secgroup_name)
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Exit context: `allow_ssh`"))
|
||||
def cleanup(self):
|
||||
for user, tenant_id in utils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
with logging.ExceptionLogger(
|
||||
LOG, _("Unable to delete secgroup: %s.") %
|
||||
user["secgroup"]["name"]):
|
||||
clients = osclients.Clients(user["endpoint"])
|
||||
clients.nova().security_groups.get(
|
||||
user["secgroup"]["id"]).delete()
|
80
rally/plugins/openstack/context/network/networks.py
Normal file
80
rally/plugins/openstack/context/network/networks.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.wrappers import network as network_wrapper
|
||||
from rally.task import context
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="network", order=350)
|
||||
class Network(context.Context):
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"start_cidr": {
|
||||
"type": "string"
|
||||
},
|
||||
"networks_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"start_cidr": "10.2.0.0/24",
|
||||
"networks_per_tenant": 1
|
||||
}
|
||||
|
||||
def __init__(self, ctx):
|
||||
super(Network, self).__init__(ctx)
|
||||
self.net_wrapper = network_wrapper.wrap(
|
||||
osclients.Clients(ctx["admin"]["endpoint"]),
|
||||
self.config)
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Enter context: `network`"))
|
||||
def setup(self):
|
||||
for user, tenant_id in (utils.iterate_per_tenants(
|
||||
self.context.get("users", []))):
|
||||
self.context["tenants"][tenant_id]["networks"] = []
|
||||
for i in range(self.config["networks_per_tenant"]):
|
||||
# NOTE(amaretskiy): add_router and subnets_num take effect
|
||||
# for Neutron only.
|
||||
# NOTE(amaretskiy): Do we need neutron subnets_num > 1 ?
|
||||
network = self.net_wrapper.create_network(tenant_id,
|
||||
add_router=True,
|
||||
subnets_num=1)
|
||||
self.context["tenants"][tenant_id]["networks"].append(network)
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Exit context: `network`"))
|
||||
def cleanup(self):
|
||||
for tenant_id, tenant_ctx in six.iteritems(self.context["tenants"]):
|
||||
for network in tenant_ctx.get("networks", []):
|
||||
with logging.ExceptionLogger(
|
||||
LOG,
|
||||
_("Failed to delete network for tenant %s")
|
||||
% tenant_id):
|
||||
self.net_wrapper.delete_network(network)
|
0
rally/plugins/openstack/context/nova/__init__.py
Normal file
0
rally/plugins/openstack/context/nova/__init__.py
Normal file
131
rally/plugins/openstack/context/nova/flavors.py
Normal file
131
rally/plugins/openstack/context/nova/flavors.py
Normal file
@ -0,0 +1,131 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 novaclient import exceptions as nova_exceptions
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
from rally.task import context
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="flavors", order=340)
|
||||
class FlavorsGenerator(context.Context):
|
||||
"""Context creates a list of flavors."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "array",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
},
|
||||
"ram": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"vcpus": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"disk": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"swap": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"ephemeral": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"extra_specs": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["name", "ram"]
|
||||
}
|
||||
}
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `flavors`"))
|
||||
def setup(self):
|
||||
"""Create list of flavors."""
|
||||
self.context["flavors"] = {}
|
||||
|
||||
clients = osclients.Clients(self.context["admin"]["endpoint"])
|
||||
for flavor_config in self.config:
|
||||
|
||||
extra_specs = flavor_config.get("extra_specs")
|
||||
|
||||
flavor_config = FlavorConfig(**flavor_config)
|
||||
try:
|
||||
flavor = clients.nova().flavors.create(**flavor_config)
|
||||
except nova_exceptions.Conflict as e:
|
||||
LOG.warning("Using already existing flavor %s" %
|
||||
flavor_config["name"])
|
||||
if logging.is_debug():
|
||||
LOG.exception(e)
|
||||
continue
|
||||
|
||||
if extra_specs:
|
||||
flavor.set_keys(extra_specs)
|
||||
|
||||
self.context["flavors"][flavor_config["name"]] = flavor.to_dict()
|
||||
LOG.debug("Created flavor with id '%s'" % flavor.id)
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `flavors`"))
|
||||
def cleanup(self):
|
||||
"""Delete created flavors."""
|
||||
clients = osclients.Clients(self.context["admin"]["endpoint"])
|
||||
for flavor in self.context["flavors"].values():
|
||||
with logging.ExceptionLogger(
|
||||
LOG, _("Can't delete flavor %s") % flavor["id"]):
|
||||
rutils.retry(3, clients.nova().flavors.delete, flavor["id"])
|
||||
LOG.debug("Flavor is deleted %s" % flavor["id"])
|
||||
|
||||
|
||||
class FlavorConfig(dict):
|
||||
def __init__(self, name, ram, vcpus=1, disk=0, swap=0, ephemeral=0,
|
||||
extra_specs=None):
|
||||
"""Flavor configuration for context and flavor & image validation code.
|
||||
|
||||
Context code uses this code to provide default values for flavor
|
||||
creation. Validation code uses this class as a Flavor instance to
|
||||
check image validity against a flavor that is to be created by
|
||||
the context.
|
||||
|
||||
:param name: name of the newly created flavor
|
||||
:param ram: RAM amount for the flavor (MBs)
|
||||
:param vcpus: VCPUs amount for the flavor
|
||||
:param disk: disk amount for the flavor (GBs)
|
||||
:param swap: swap amount for the flavor (MBs)
|
||||
:param ephemeral: ephemeral disk amount for the flavor (GBs)
|
||||
:param extra_specs: is ignored
|
||||
"""
|
||||
super(FlavorConfig, self).__init__(
|
||||
name=name, ram=ram, vcpus=vcpus, disk=disk,
|
||||
swap=swap, ephemeral=ephemeral)
|
||||
self.__dict__.update(self)
|
61
rally/plugins/openstack/context/nova/keypairs.py
Normal file
61
rally/plugins/openstack/context/nova/keypairs.py
Normal file
@ -0,0 +1,61 @@
|
||||
# Copyright 2014: Rackspace UK
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import novaclient.exceptions
|
||||
|
||||
from rally.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.task import context
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="keypair", order=310)
|
||||
class Keypair(context.Context):
|
||||
KEYPAIR_NAME = "rally_ssh_key"
|
||||
|
||||
def _generate_keypair(self, endpoint):
|
||||
keypair_name = "%s_%s" % (
|
||||
self.KEYPAIR_NAME, self.context["task"]["uuid"])
|
||||
|
||||
nova_client = osclients.Clients(endpoint).nova()
|
||||
|
||||
# NOTE(hughsaunders): If keypair exists, it must be deleted as we can't
|
||||
# retrieve the private key
|
||||
try:
|
||||
nova_client.keypairs.delete(keypair_name)
|
||||
except novaclient.exceptions.NotFound:
|
||||
pass
|
||||
|
||||
keypair = nova_client.keypairs.create(keypair_name)
|
||||
return {"private": keypair.private_key,
|
||||
"public": keypair.public_key,
|
||||
"name": keypair_name,
|
||||
"id": keypair.id}
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Enter context: `keypair`"))
|
||||
def setup(self):
|
||||
for user in self.context["users"]:
|
||||
user["keypair"] = self._generate_keypair(user["endpoint"])
|
||||
|
||||
@utils.log_task_wrapper(LOG.info, _("Exit context: `keypair`"))
|
||||
def cleanup(self):
|
||||
# TODO(boris-42): Delete only resources created by this context
|
||||
resource_manager.cleanup(names=["nova.keypairs"],
|
||||
users=self.context.get("users", []))
|
108
rally/plugins/openstack/context/nova/servers.py
Normal file
108
rally/plugins/openstack/context/nova/servers.py
Normal file
@ -0,0 +1,108 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.common.i18n import _
|
||||
from rally.common import log as logging
|
||||
from rally.common import utils as rutils
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
from rally.plugins.openstack.context.cleanup import manager as resource_manager
|
||||
from rally.plugins.openstack.scenarios.nova import utils as nova_utils
|
||||
from rally.task import context
|
||||
from rally.task import types
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.context(name="servers", order=430)
|
||||
class ServerGenerator(context.Context):
|
||||
"""Context class for adding temporary servers for benchmarks.
|
||||
|
||||
Servers are added for each tenant.
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"flavor": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"servers_per_tenant": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
},
|
||||
"required": ["image", "flavor"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"servers_per_tenant": 5
|
||||
}
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `Servers`"))
|
||||
def setup(self):
|
||||
image = self.config["image"]
|
||||
flavor = self.config["flavor"]
|
||||
servers_per_tenant = self.config["servers_per_tenant"]
|
||||
|
||||
clients = osclients.Clients(self.context["users"][0]["endpoint"])
|
||||
image_id = types.ImageResourceType.transform(clients=clients,
|
||||
resource_config=image)
|
||||
flavor_id = types.FlavorResourceType.transform(clients=clients,
|
||||
resource_config=flavor)
|
||||
|
||||
for user, tenant_id in rutils.iterate_per_tenants(
|
||||
self.context["users"]):
|
||||
LOG.debug("Booting servers for user tenant %s "
|
||||
% (user["tenant_id"]))
|
||||
nova_scenario = nova_utils.NovaScenario({"user": user})
|
||||
|
||||
LOG.debug("Calling _boot_servers with image_id=%(image_id)s "
|
||||
"flavor_id=%(flavor_id)s "
|
||||
"servers_per_tenant=%(servers_per_tenant)s"
|
||||
% {"image_id": image_id,
|
||||
"flavor_id": flavor_id,
|
||||
"servers_per_tenant": servers_per_tenant})
|
||||
|
||||
servers = nova_scenario._boot_servers(image_id, flavor_id,
|
||||
servers_per_tenant)
|
||||
|
||||
current_servers = [server.id for server in servers]
|
||||
|
||||
LOG.debug("Adding booted servers %s to context"
|
||||
% current_servers)
|
||||
|
||||
self.context["tenants"][tenant_id][
|
||||
"servers"] = current_servers
|
||||
|
||||
@rutils.log_task_wrapper(LOG.info, _("Exit context: `Servers`"))
|
||||
def cleanup(self):
|
||||
resource_manager.cleanup(names=["nova.servers"],
|
||||
users=self.context.get("users", []))
|
124
tests/unit/plugins/openstack/context/ceilometer/test_samples.py
Normal file
124
tests/unit/plugins/openstack/context/ceilometer/test_samples.py
Normal file
@ -0,0 +1,124 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.ceilometer import samples
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.ceilometer"
|
||||
|
||||
|
||||
class CeilometerSampleGeneratorTestCase(test.TestCase):
|
||||
|
||||
def _gen_tenants(self, count):
|
||||
tenants = {}
|
||||
for id_ in range(count):
|
||||
tenants[str(id_)] = {"name": str(id_)}
|
||||
return tenants
|
||||
|
||||
def _gen_context(self, tenants_count, users_per_tenant,
|
||||
resources_per_tenant, samples_per_resource):
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants.keys():
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": mock.MagicMock()})
|
||||
context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": tenants_count,
|
||||
"users_per_tenant": users_per_tenant,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"ceilometer": {
|
||||
"counter_name": "fake-counter-name",
|
||||
"counter_type": "fake-counter-type",
|
||||
"counter_unit": "fake-counter-unit",
|
||||
"counter_volume": 100,
|
||||
"resources_per_tenant": resources_per_tenant,
|
||||
"samples_per_resource": samples_per_resource
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
return tenants, context
|
||||
|
||||
def test_init(self):
|
||||
context = {}
|
||||
context["task"] = mock.MagicMock()
|
||||
context["config"] = {
|
||||
"ceilometer": {
|
||||
"counter_name": "cpu_util",
|
||||
"counter_type": "gauge",
|
||||
"counter_unit": "instance",
|
||||
"counter_volume": 1.0,
|
||||
"resources_per_tenant": 5,
|
||||
"samples_per_resource": 5
|
||||
}
|
||||
}
|
||||
|
||||
inst = samples.CeilometerSampleGenerator(context)
|
||||
self.assertEqual(inst.config, context["config"]["ceilometer"])
|
||||
|
||||
@mock.patch("%s.samples.ceilo_utils.CeilometerScenario._create_sample"
|
||||
% CTX)
|
||||
def test_setup(self, mock_ceilometer_scenario__create_sample):
|
||||
tenants_count = 2
|
||||
users_per_tenant = 2
|
||||
resources_per_tenant = 2
|
||||
samples_per_resource = 2
|
||||
|
||||
tenants, real_context = self._gen_context(
|
||||
tenants_count, users_per_tenant,
|
||||
resources_per_tenant, samples_per_resource)
|
||||
|
||||
sample = {
|
||||
"counter_name": "fake-counter-name",
|
||||
"counter_type": "fake-counter-type",
|
||||
"counter_unit": "fake-counter-unit",
|
||||
"counter_volume": 100,
|
||||
"resource_id": "fake-resource-id"
|
||||
}
|
||||
|
||||
new_context = copy.deepcopy(real_context)
|
||||
for id_ in tenants.keys():
|
||||
new_context["tenants"][id_].setdefault("samples", [])
|
||||
new_context["tenants"][id_].setdefault("resources", [])
|
||||
for i in range(resources_per_tenant):
|
||||
for j in range(samples_per_resource):
|
||||
new_context["tenants"][id_]["samples"].append(sample)
|
||||
new_context["tenants"][id_]["resources"].append(
|
||||
sample["resource_id"])
|
||||
|
||||
mock_ceilometer_scenario__create_sample.return_value = [
|
||||
mock.MagicMock(to_dict=lambda: sample, **sample)]
|
||||
|
||||
ceilometer_ctx = samples.CeilometerSampleGenerator(real_context)
|
||||
ceilometer_ctx.setup()
|
||||
self.assertEqual(new_context, ceilometer_ctx.context)
|
||||
|
||||
def test_cleanup(self):
|
||||
tenants, context = self._gen_context(2, 5, 3, 3)
|
||||
ceilometer_ctx = samples.CeilometerSampleGenerator(context)
|
||||
ceilometer_ctx.cleanup()
|
134
tests/unit/plugins/openstack/context/cinder/test_volumes.py
Normal file
134
tests/unit/plugins/openstack/context/cinder/test_volumes.py
Normal file
@ -0,0 +1,134 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.cinder import volumes
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context"
|
||||
SCN = "rally.plugins.openstack.scenarios"
|
||||
|
||||
|
||||
class VolumeGeneratorTestCase(test.ScenarioTestCase):
|
||||
|
||||
def _gen_tenants(self, count):
|
||||
tenants = {}
|
||||
for id_ in range(count):
|
||||
tenants[str(id_)] = {"name": str(id_)}
|
||||
return tenants
|
||||
|
||||
def test_init(self):
|
||||
context = {}
|
||||
context["task"] = mock.MagicMock()
|
||||
context["config"] = {
|
||||
"volumes": {
|
||||
"size": 1,
|
||||
"volumes_per_tenant": 5,
|
||||
}
|
||||
}
|
||||
|
||||
inst = volumes.VolumeGenerator(context)
|
||||
self.assertEqual(inst.config, context["config"]["volumes"])
|
||||
|
||||
@mock.patch("%s.cinder.utils.CinderScenario._create_volume" % SCN,
|
||||
return_value=fakes.FakeVolume(id="uuid"))
|
||||
def test_setup(self, mock_cinder_scenario__create_volume):
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
volumes_per_tenant = 5
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants.keys():
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": mock.MagicMock()})
|
||||
|
||||
real_context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 5,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"volumes": {
|
||||
"size": 1,
|
||||
"volumes_per_tenant": volumes_per_tenant,
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
new_context = copy.deepcopy(real_context)
|
||||
for id_ in tenants.keys():
|
||||
new_context["tenants"][id_].setdefault("volumes", [])
|
||||
for i in range(volumes_per_tenant):
|
||||
new_context["tenants"][id_]["volumes"].append({"id": "uuid"})
|
||||
|
||||
volumes_ctx = volumes.VolumeGenerator(real_context)
|
||||
volumes_ctx.setup()
|
||||
self.assertEqual(new_context, real_context)
|
||||
|
||||
@mock.patch("%s.cinder.volumes.resource_manager.cleanup" % CTX)
|
||||
def test_cleanup(self, mock_cleanup):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
volumes_per_tenant = 5
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants.keys():
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": "endpoint"})
|
||||
tenants[id_].setdefault("volumes", [])
|
||||
for j in range(volumes_per_tenant):
|
||||
tenants[id_]["volumes"].append({"id": "uuid"})
|
||||
|
||||
context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 5,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"volumes": {
|
||||
"size": 1,
|
||||
"volumes_per_tenant": 5,
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
volumes_ctx = volumes.VolumeGenerator(context)
|
||||
volumes_ctx.cleanup()
|
||||
|
||||
mock_cleanup.assert_called_once_with(names=["cinder.volumes"],
|
||||
users=context["users"])
|
163
tests/unit/plugins/openstack/context/glance/test_images.py
Normal file
163
tests/unit/plugins/openstack/context/glance/test_images.py
Normal file
@ -0,0 +1,163 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
import jsonschema
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.glance import images
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.glance"
|
||||
SCN = "rally.plugins.openstack.scenarios.glance"
|
||||
|
||||
|
||||
class ImageGeneratorTestCase(test.ScenarioTestCase):
|
||||
|
||||
def _gen_tenants(self, count):
|
||||
tenants = {}
|
||||
for id_ in range(count):
|
||||
tenants[str(id_)] = {"name": str(id_)}
|
||||
return tenants
|
||||
|
||||
@mock.patch("%s.images.context.Context.__init__" % CTX)
|
||||
def test_init(self, mock_context___init__):
|
||||
context = {}
|
||||
context["task"] = mock.MagicMock()
|
||||
context["config"] = {
|
||||
"images": {
|
||||
"image_url": "mock_url",
|
||||
"image_type": "qcow2",
|
||||
"image_container": "bare",
|
||||
"images_per_tenant": 4,
|
||||
"image_name": "some_name",
|
||||
"min_ram": 128,
|
||||
"min_disk": 1,
|
||||
}
|
||||
}
|
||||
|
||||
images.ImageGenerator(context)
|
||||
mock_context___init__.assert_called_once_with(context)
|
||||
|
||||
def test_init_validation(self):
|
||||
context = {}
|
||||
context["task"] = mock.MagicMock()
|
||||
context["config"] = {
|
||||
"images": {
|
||||
"image_url": "mock_url"
|
||||
}
|
||||
}
|
||||
|
||||
self.assertRaises(jsonschema.ValidationError,
|
||||
images.ImageGenerator.validate, context)
|
||||
|
||||
@mock.patch("%s.utils.GlanceScenario._create_image" % SCN,
|
||||
return_value=fakes.FakeImage(id="uuid"))
|
||||
def test_setup(self, mock_glance_scenario__create_image):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
images_per_tenant = 5
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants:
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": mock.MagicMock()})
|
||||
|
||||
real_context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": tenants_count,
|
||||
"users_per_tenant": users_per_tenant,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"images": {
|
||||
"image_url": "mock_url",
|
||||
"image_type": "qcow2",
|
||||
"image_container": "bare",
|
||||
"images_per_tenant": images_per_tenant,
|
||||
"image_name": "some_name",
|
||||
"min_ram": 128,
|
||||
"min_disk": 1,
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
new_context = copy.deepcopy(real_context)
|
||||
for id_ in new_context["tenants"].keys():
|
||||
new_context["tenants"][id_].setdefault("images", [])
|
||||
for j in range(images_per_tenant):
|
||||
new_context["tenants"][id_]["images"].append("uuid")
|
||||
|
||||
images_ctx = images.ImageGenerator(real_context)
|
||||
images_ctx.setup()
|
||||
self.assertEqual(new_context, real_context)
|
||||
|
||||
@mock.patch("%s.images.resource_manager.cleanup" % CTX)
|
||||
def test_cleanup(self, mock_cleanup):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
images_per_tenant = 5
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants:
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": "endpoint"})
|
||||
tenants[id_].setdefault("images", [])
|
||||
for j in range(images_per_tenant):
|
||||
tenants[id_]["images"].append("uuid")
|
||||
|
||||
context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 5,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"images": {
|
||||
"image_url": "mock_url",
|
||||
"image_type": "qcow2",
|
||||
"image_container": "bare",
|
||||
"images_per_tenant": 5,
|
||||
"image_name": "some_name",
|
||||
"min_ram": 128,
|
||||
"min_disk": 1,
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
images_ctx = images.ImageGenerator(context)
|
||||
images_ctx.cleanup()
|
||||
mock_cleanup.assert_called_once_with(names=["glance.images"],
|
||||
users=context["users"])
|
97
tests/unit/plugins/openstack/context/heat/test_stacks.py
Normal file
97
tests/unit/plugins/openstack/context/heat/test_stacks.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright 2015: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.heat import stacks
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context"
|
||||
SCN = "rally.plugins.openstack.scenarios"
|
||||
|
||||
|
||||
class TestStackGenerator(test.ScenarioTestCase):
|
||||
|
||||
def _gen_tenants(self, count):
|
||||
tenants = {}
|
||||
for id_ in range(count):
|
||||
tenants[str(id_)] = dict(name=str(id_))
|
||||
return tenants
|
||||
|
||||
def test_init(self):
|
||||
context = {
|
||||
"task": mock.MagicMock(),
|
||||
"config": {
|
||||
"stacks": {
|
||||
"stacks_per_tenant": 1,
|
||||
"resources_per_stack": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inst = stacks.StackGenerator(context)
|
||||
self.assertEqual(inst.config, context["config"]["stacks"])
|
||||
|
||||
@mock.patch("%s.heat.utils.HeatScenario._create_stack" % SCN,
|
||||
return_value=fakes.FakeStack(id="uuid"))
|
||||
def test_setup(self, mock_heat_scenario__create_stack):
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
stacks_per_tenant = 1
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for ten_id in tenants:
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": ten_id,
|
||||
"endpoint": mock.MagicMock()})
|
||||
|
||||
context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": tenants_count,
|
||||
"users_per_tenant": users_per_tenant,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"stacks": {
|
||||
"stacks_per_tenant": stacks_per_tenant,
|
||||
"resources_per_stack": 1
|
||||
}
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
stack_ctx = stacks.StackGenerator(context)
|
||||
stack_ctx.setup()
|
||||
self.assertEqual(tenants_count * stacks_per_tenant,
|
||||
mock_heat_scenario__create_stack.call_count)
|
||||
# check that stack ids have been saved in context
|
||||
for ten_id in context["tenants"].keys():
|
||||
self.assertEqual(stacks_per_tenant,
|
||||
len(context["tenants"][ten_id]["stacks"]))
|
||||
|
||||
@mock.patch("%s.heat.stacks.resource_manager.cleanup" % CTX)
|
||||
def test_cleanup(self, mock_cleanup):
|
||||
context = {
|
||||
"task": mock.MagicMock(),
|
||||
"users": mock.MagicMock()
|
||||
}
|
||||
stack_ctx = stacks.StackGenerator(context)
|
||||
stack_ctx.cleanup()
|
||||
mock_cleanup.assert_called_once_with(names=["heat.stacks"],
|
||||
users=context["users"])
|
131
tests/unit/plugins/openstack/context/keystone/test_roles.py
Normal file
131
tests/unit/plugins/openstack/context/keystone/test_roles.py
Normal file
@ -0,0 +1,131 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from rally import exceptions
|
||||
from rally.plugins.openstack.context.keystone import roles
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.keystone.roles"
|
||||
|
||||
|
||||
class RoleGeneratorTestCase(test.TestCase):
|
||||
|
||||
def create_default_roles_and_patch_add_remove_functions(self, fc):
|
||||
fc.keystone().roles.add_user_role = mock.MagicMock()
|
||||
fc.keystone().roles.remove_user_role = mock.MagicMock()
|
||||
fc.keystone().roles.create("r1", "test_role1")
|
||||
fc.keystone().roles.create("r2", "test_role2")
|
||||
self.assertEqual(2, len(fc.keystone().roles.list()))
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
return {
|
||||
"config": {
|
||||
"roles": [
|
||||
"test_role1",
|
||||
"test_role2"
|
||||
]
|
||||
},
|
||||
"admin": {"endpoint": mock.MagicMock()},
|
||||
"task": mock.MagicMock()
|
||||
}
|
||||
|
||||
@mock.patch("%s.osclients" % CTX)
|
||||
def test_add_role(self, mock_osclients):
|
||||
fc = fakes.FakeClients()
|
||||
mock_osclients.Clients.return_value = fc
|
||||
self.create_default_roles_and_patch_add_remove_functions(fc)
|
||||
|
||||
ctx = roles.RoleGenerator(self.context)
|
||||
ctx.context["users"] = [{"id": "u1", "tenant_id": "t1"},
|
||||
{"id": "u2", "tenant_id": "t2"}]
|
||||
result = ctx._add_role(mock.MagicMock(),
|
||||
self.context["config"]["roles"][0])
|
||||
|
||||
expected = {"id": "r1", "name": "test_role1"}
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch("%s.osclients" % CTX)
|
||||
def test_add_role_which_does_not_exist(self, mock_osclients):
|
||||
fc = fakes.FakeClients()
|
||||
mock_osclients.Clients.return_value = fc
|
||||
self.create_default_roles_and_patch_add_remove_functions(fc)
|
||||
|
||||
ctx = roles.RoleGenerator(self.context)
|
||||
ctx.context["users"] = [{"id": "u1", "tenant_id": "t1"},
|
||||
{"id": "u2", "tenant_id": "t2"}]
|
||||
ex = self.assertRaises(exceptions.NoSuchRole, ctx._add_role,
|
||||
mock.MagicMock(), "unknown_role")
|
||||
|
||||
expected = "There is no role with name `unknown_role`."
|
||||
self.assertEqual(expected, str(ex))
|
||||
|
||||
@mock.patch("%s.osclients" % CTX)
|
||||
def test_remove_role(self, mock_osclients):
|
||||
role = mock.MagicMock()
|
||||
fc = fakes.FakeClients()
|
||||
mock_osclients.Clients.return_value = fc
|
||||
self.create_default_roles_and_patch_add_remove_functions(fc)
|
||||
|
||||
ctx = roles.RoleGenerator(self.context)
|
||||
ctx.context["users"] = [{"id": "u1", "tenant_id": "t1"},
|
||||
{"id": "u2", "tenant_id": "t2"}]
|
||||
ctx._remove_role(mock.MagicMock(), role)
|
||||
calls = [
|
||||
mock.call("u1", role["id"], tenant="t1"),
|
||||
mock.call("u2", role["id"], tenant="t2"),
|
||||
]
|
||||
mock_keystone = mock_osclients.Clients().keystone()
|
||||
mock_keystone.roles.remove_user_role.assert_has_calls(calls)
|
||||
|
||||
@mock.patch("%s.osclients" % CTX)
|
||||
def test_setup_and_cleanup(self, mock_osclients):
|
||||
fc = fakes.FakeClients()
|
||||
mock_osclients.Clients.return_value = fc
|
||||
self.create_default_roles_and_patch_add_remove_functions(fc)
|
||||
|
||||
with roles.RoleGenerator(self.context) as ctx:
|
||||
ctx.context["users"] = [{"id": "u1", "tenant_id": "t1"},
|
||||
{"id": "u2", "tenant_id": "t2"}]
|
||||
|
||||
ctx.setup()
|
||||
calls = [
|
||||
mock.call("u1", "r1", tenant="t1"),
|
||||
mock.call("u2", "r1", tenant="t2"),
|
||||
mock.call("u1", "r2", tenant="t1"),
|
||||
mock.call("u2", "r2", tenant="t2")
|
||||
]
|
||||
fc.keystone().roles.add_user_role.assert_has_calls(calls)
|
||||
self.assertEqual(
|
||||
4, fc.keystone().roles.add_user_role.call_count)
|
||||
self.assertEqual(
|
||||
0, fc.keystone().roles.remove_user_role.call_count)
|
||||
self.assertEqual(2, len(ctx.context["roles"]))
|
||||
self.assertEqual(2, len(fc.keystone().roles.list()))
|
||||
|
||||
# Cleanup (called by content manager)
|
||||
self.assertEqual(2, len(fc.keystone().roles.list()))
|
||||
self.assertEqual(4, fc.keystone().roles.add_user_role.call_count)
|
||||
self.assertEqual(4, fc.keystone().roles.remove_user_role.call_count)
|
||||
calls = [
|
||||
mock.call("u1", "r1", tenant="t1"),
|
||||
mock.call("u2", "r1", tenant="t2"),
|
||||
mock.call("u1", "r2", tenant="t1"),
|
||||
mock.call("u2", "r2", tenant="t2")
|
||||
]
|
||||
fc.keystone().roles.remove_user_role.assert_has_calls(calls)
|
358
tests/unit/plugins/openstack/context/keystone/test_users.py
Normal file
358
tests/unit/plugins/openstack/context/keystone/test_users.py
Normal file
@ -0,0 +1,358 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from rally import consts
|
||||
from rally import exceptions
|
||||
from rally import objects
|
||||
from rally.plugins.openstack.context.keystone import users
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.keystone.users"
|
||||
|
||||
|
||||
class UserGeneratorTestCase(test.TestCase):
|
||||
|
||||
tenants_num = 1
|
||||
users_per_tenant = 5
|
||||
users_num = tenants_num * users_per_tenant
|
||||
threads = 10
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
return {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": self.tenants_num,
|
||||
"users_per_tenant": self.users_per_tenant,
|
||||
"resource_management_workers": self.threads,
|
||||
}
|
||||
},
|
||||
"admin": {"endpoint": mock.MagicMock()},
|
||||
"task": {"uuid": "task_id"}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(UserGeneratorTestCase, self).setUp()
|
||||
self.osclients_patcher = mock.patch("%s.osclients" % CTX)
|
||||
self.osclients = self.osclients_patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.osclients_patcher.stop()
|
||||
super(UserGeneratorTestCase, self).tearDown()
|
||||
|
||||
@mock.patch("%s.network.wrap" % CTX)
|
||||
def test__remove_default_security_group_not_needed(self, mock_wrap):
|
||||
services = {"compute": consts.Service.NOVA}
|
||||
self.osclients.Clients().services.return_value = services
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator._remove_default_security_group()
|
||||
self.assertFalse(mock_wrap.called)
|
||||
|
||||
@mock.patch("%s.network.wrap" % CTX)
|
||||
def test__remove_default_security_group_neutron_no_sg(self, mock_wrap):
|
||||
net_wrapper = mock.Mock(SERVICE_IMPL=consts.Service.NEUTRON)
|
||||
net_wrapper.supports_security_group.return_value = (False, None)
|
||||
mock_wrap.return_value = net_wrapper
|
||||
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
|
||||
admin_clients = mock.Mock()
|
||||
admin_clients.services.return_value = {
|
||||
"compute": consts.Service.NOVA,
|
||||
"neutron": consts.Service.NEUTRON}
|
||||
user_clients = [mock.Mock(), mock.Mock()]
|
||||
self.osclients.Clients.side_effect = [admin_clients] + user_clients
|
||||
|
||||
user_generator._remove_default_security_group()
|
||||
|
||||
mock_wrap.assert_called_once_with(admin_clients)
|
||||
net_wrapper.supports_security_group.assert_called_once_with()
|
||||
|
||||
@mock.patch("rally.common.utils.iterate_per_tenants")
|
||||
@mock.patch("%s.network" % CTX)
|
||||
@mock.patch("rally.task.utils.check_service_status",
|
||||
return_value=False)
|
||||
def test__remove_default_security_group(
|
||||
self, mock_check_service_status, mock_network,
|
||||
mock_iterate_per_tenants):
|
||||
net_wrapper = mock.Mock(SERVICE_IMPL=consts.Service.NEUTRON)
|
||||
net_wrapper.supports_security_group.return_value = (True, None)
|
||||
mock_network.wrap.return_value = net_wrapper
|
||||
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
|
||||
admin_clients = mock.Mock()
|
||||
admin_clients.services.return_value = {
|
||||
"compute": consts.Service.NOVA,
|
||||
"neutron": consts.Service.NEUTRON}
|
||||
user_clients = [mock.Mock(), mock.Mock()]
|
||||
self.osclients.Clients.side_effect = [admin_clients] + user_clients
|
||||
|
||||
mock_iterate_per_tenants.return_value = [
|
||||
(mock.MagicMock(), "t1"),
|
||||
(mock.MagicMock(), "t2")]
|
||||
|
||||
user_generator._remove_default_security_group()
|
||||
|
||||
mock_network.wrap.assert_called_once_with(admin_clients)
|
||||
|
||||
mock_iterate_per_tenants.assert_called_once_with(
|
||||
user_generator.context["users"])
|
||||
expected = [mock.call(user_generator.endpoint)] + [
|
||||
mock.call(u["endpoint"])
|
||||
for u, t in mock_iterate_per_tenants.return_value]
|
||||
self.osclients.Clients.assert_has_calls(expected, any_order=True)
|
||||
|
||||
expected_deletes = []
|
||||
for clients in user_clients:
|
||||
user_nova = clients.nova.return_value
|
||||
user_nova.security_groups.find.assert_called_once_with(
|
||||
name="default")
|
||||
expected_deletes.append(
|
||||
mock.call(user_nova.security_groups.find.return_value.id))
|
||||
|
||||
nova_admin = admin_clients.neutron.return_value
|
||||
nova_admin.delete_security_group.assert_has_calls(expected_deletes,
|
||||
any_order=True)
|
||||
|
||||
@mock.patch("rally.task.utils.check_service_status",
|
||||
return_value=True)
|
||||
def test__remove_associated_networks(self, mock_check_service_status):
|
||||
def fake_get_network(req_network):
|
||||
for network in networks:
|
||||
if network.project_id == req_network.project_id:
|
||||
return network
|
||||
|
||||
networks = [mock.MagicMock(project_id="t1"),
|
||||
mock.MagicMock(project_id="t4")]
|
||||
nova_admin = mock.MagicMock()
|
||||
clients = mock.MagicMock()
|
||||
self.osclients.Clients.return_value = clients
|
||||
clients.services.return_value = {"compute": "nova"}
|
||||
clients.nova.return_value = nova_admin
|
||||
nova_admin.networks.list.return_value = networks
|
||||
nova_admin.networks.get = fake_get_network
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
|
||||
"t2": dict(id="t2", name="t2")}
|
||||
user_generator._remove_associated_networks()
|
||||
mock_check_service_status.assert_called_once_with(mock.ANY,
|
||||
"nova-network")
|
||||
nova_admin.networks.disassociate.assert_called_once_with(networks[0])
|
||||
|
||||
@mock.patch("rally.task.utils.check_service_status",
|
||||
return_value=True)
|
||||
def test__remove_associated_networks_failure(self,
|
||||
mock_check_service_status):
|
||||
def fake_get_network(req_network):
|
||||
for network in networks:
|
||||
if network.project_id == req_network.project_id:
|
||||
return network
|
||||
|
||||
networks = [mock.MagicMock(project_id="t1"),
|
||||
mock.MagicMock(project_id="t4")]
|
||||
nova_admin = mock.MagicMock()
|
||||
clients = mock.MagicMock()
|
||||
self.osclients.Clients.return_value = clients
|
||||
clients.services.return_value = {"compute": "nova"}
|
||||
clients.nova.return_value = nova_admin
|
||||
nova_admin.networks.list.return_value = networks
|
||||
nova_admin.networks.get = fake_get_network
|
||||
nova_admin.networks.disassociate.side_effect = Exception()
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
|
||||
"t2": dict(id="t2", name="t2")}
|
||||
user_generator._remove_associated_networks()
|
||||
mock_check_service_status.assert_called_once_with(mock.ANY,
|
||||
"nova-network")
|
||||
nova_admin.networks.disassociate.assert_called_once_with(networks[0])
|
||||
|
||||
@mock.patch("%s.broker.time.sleep" % CTX)
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test__create_tenants(self, mock_keystone, mock_sleep):
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator.config["tenants"] = 1
|
||||
tenants = user_generator._create_tenants()
|
||||
self.assertEqual(1, len(tenants))
|
||||
id, tenant = tenants.popitem()
|
||||
self.assertIn("name", tenant)
|
||||
|
||||
@mock.patch("%s.broker.time.sleep" % CTX)
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test__create_users(self, mock_keystone, mock_sleep):
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
|
||||
"t2": dict(id="t2", name="t2")}
|
||||
user_generator.config["users_per_tenant"] = 2
|
||||
users_ = user_generator._create_users()
|
||||
self.assertEqual(4, len(users_))
|
||||
for user in users_:
|
||||
self.assertIn("id", user)
|
||||
self.assertIn("endpoint", user)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test__delete_tenants(self, mock_keystone):
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
|
||||
"t2": dict(id="t2", name="t2")}
|
||||
user_generator._delete_tenants()
|
||||
self.assertEqual(len(user_generator.context["tenants"]), 0)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test__delete_tenants_failure(self, mock_keystone):
|
||||
wrapped_keystone = mock_keystone.wrap.return_value
|
||||
wrapped_keystone.delete_project.side_effect = Exception()
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user_generator.context["tenants"] = {"t1": dict(id="t1", name="t1"),
|
||||
"t2": dict(id="t2", name="t2")}
|
||||
user_generator._delete_tenants()
|
||||
self.assertEqual(len(user_generator.context["tenants"]), 0)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test__delete_users(self, mock_keystone):
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user1 = mock.MagicMock()
|
||||
user2 = mock.MagicMock()
|
||||
user_generator.context["users"] = [user1, user2]
|
||||
user_generator._delete_users()
|
||||
self.assertEqual(len(user_generator.context["users"]), 0)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test__delete_users_failure(self, mock_keystone):
|
||||
wrapped_keystone = mock_keystone.wrap.return_value
|
||||
wrapped_keystone.delete_user.side_effect = Exception()
|
||||
user_generator = users.UserGenerator(self.context)
|
||||
user1 = mock.MagicMock()
|
||||
user2 = mock.MagicMock()
|
||||
user_generator.context["users"] = [user1, user2]
|
||||
user_generator._delete_users()
|
||||
self.assertEqual(len(user_generator.context["users"]), 0)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test_setup_and_cleanup(self, mock_keystone):
|
||||
wrapped_keystone = mock.MagicMock()
|
||||
mock_keystone.wrap.return_value = wrapped_keystone
|
||||
with users.UserGenerator(self.context) as ctx:
|
||||
|
||||
ctx.setup()
|
||||
|
||||
self.assertEqual(len(ctx.context["users"]),
|
||||
self.users_num)
|
||||
self.assertEqual(len(ctx.context["tenants"]),
|
||||
self.tenants_num)
|
||||
|
||||
# Cleanup (called by content manager)
|
||||
self.assertEqual(len(ctx.context["users"]), 0)
|
||||
self.assertEqual(len(ctx.context["tenants"]), 0)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test_setup_and_cleanup_failure(self, mock_keystone):
|
||||
wrapped_keystone = mock_keystone.wrap.return_value
|
||||
wrapped_keystone.create_user.side_effect = Exception()
|
||||
with users.UserGenerator(self.context) as ctx:
|
||||
self.assertRaises(exceptions.ContextSetupFailure, ctx.setup)
|
||||
|
||||
# Ensure that tenants get deleted anyway
|
||||
self.assertEqual(len(ctx.context["tenants"]), 0)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test_users_and_tenants_in_context(self, mock_keystone):
|
||||
wrapped_keystone = mock.MagicMock()
|
||||
mock_keystone.wrap.return_value = wrapped_keystone
|
||||
task = {"uuid": "abcdef"}
|
||||
|
||||
config = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 2,
|
||||
"resource_management_workers": 1
|
||||
}
|
||||
},
|
||||
"admin": {"endpoint": mock.MagicMock()},
|
||||
"task": task
|
||||
}
|
||||
|
||||
user_list = [mock.MagicMock(id="id_%d" % i)
|
||||
for i in range(self.users_num)]
|
||||
wrapped_keystone.create_user.side_effect = user_list
|
||||
|
||||
with users.UserGenerator(config) as ctx:
|
||||
ctx.setup()
|
||||
|
||||
create_tenant_calls = []
|
||||
for i, t in enumerate(ctx.context["tenants"]):
|
||||
pattern = users.UserGenerator.PATTERN_TENANT
|
||||
create_tenant_calls.append(
|
||||
mock.call(pattern % {"task_id": task["uuid"], "iter": i},
|
||||
ctx.config["project_domain"]))
|
||||
|
||||
for user in ctx.context["users"]:
|
||||
self.assertEqual(set(["id", "endpoint", "tenant_id"]),
|
||||
set(user.keys()))
|
||||
|
||||
tenants_ids = []
|
||||
for t in ctx.context["tenants"].keys():
|
||||
tenants_ids.append(t)
|
||||
|
||||
for (user, tenant_id, orig_user) in zip(ctx.context["users"],
|
||||
tenants_ids, user_list):
|
||||
self.assertEqual(user["id"], orig_user.id)
|
||||
self.assertEqual(user["tenant_id"], tenant_id)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test_users_contains_correct_endpoint_type(self, mock_keystone):
|
||||
endpoint = objects.Endpoint("foo_url", "foo", "foo_pass",
|
||||
endpoint_type=consts.EndpointType.INTERNAL)
|
||||
config = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 2,
|
||||
"resource_management_workers": 1
|
||||
}
|
||||
},
|
||||
"admin": {"endpoint": endpoint},
|
||||
"task": {"uuid": "task_id"}
|
||||
}
|
||||
|
||||
user_generator = users.UserGenerator(config)
|
||||
users_ = user_generator._create_users()
|
||||
|
||||
for user in users_:
|
||||
self.assertEqual("internal", user["endpoint"].endpoint_type)
|
||||
|
||||
@mock.patch("%s.keystone" % CTX)
|
||||
def test_users_contains_default_endpoint_type(self, mock_keystone):
|
||||
endpoint = objects.Endpoint("foo_url", "foo", "foo_pass")
|
||||
config = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 2,
|
||||
"resource_management_workers": 1
|
||||
}
|
||||
},
|
||||
"admin": {"endpoint": endpoint},
|
||||
"task": {"uuid": "task_id"}
|
||||
}
|
||||
|
||||
user_generator = users.UserGenerator(config)
|
||||
users_ = user_generator._create_users()
|
||||
|
||||
for user in users_:
|
||||
self.assertEqual("public", user["endpoint"].endpoint_type)
|
@ -0,0 +1,93 @@
|
||||
# Copyright 2015: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.murano import murano_packages
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.murano.murano_packages"
|
||||
|
||||
|
||||
class MuranoGeneratorTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MuranoGeneratorTestCase, self).setUp()
|
||||
|
||||
@staticmethod
|
||||
def _get_context():
|
||||
return {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 1,
|
||||
"concurrent": 1,
|
||||
},
|
||||
"murano_packages": {
|
||||
"app_package": (
|
||||
"rally-jobs/extra/murano/"
|
||||
"applications/HelloReporter/"
|
||||
"io.murano.apps.HelloReporter.zip")
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": [
|
||||
{
|
||||
"id": "user_0",
|
||||
"tenant_id": "tenant_0",
|
||||
"endpoint": "endpoint"
|
||||
},
|
||||
{
|
||||
"id": "user_1",
|
||||
"tenant_id": "tenant_1",
|
||||
"endpoint": "endpoint"
|
||||
}
|
||||
],
|
||||
"tenants": {
|
||||
"tenant_0": {"name": "tenant_0_name"},
|
||||
"tenant_1": {"name": "tenant_1_name"}
|
||||
}
|
||||
}
|
||||
|
||||
@mock.patch("%s.osclients" % CTX)
|
||||
def test_setup(self, mock_osclients):
|
||||
mock_app = mock.MagicMock(id="fake_app_id")
|
||||
(mock_osclients.Clients().murano().
|
||||
packages.create.return_value) = mock_app
|
||||
|
||||
murano_ctx = murano_packages.PackageGenerator(self._get_context())
|
||||
murano_ctx.setup()
|
||||
|
||||
self.assertEqual(2, len(murano_ctx.context["tenants"]))
|
||||
tenant_id = murano_ctx.context["users"][0]["tenant_id"]
|
||||
self.assertEqual([mock_app],
|
||||
murano_ctx.context["tenants"][tenant_id]["packages"])
|
||||
|
||||
@mock.patch("%s.osclients" % CTX)
|
||||
@mock.patch("%s.resource_manager.cleanup" % CTX)
|
||||
def test_cleanup(self, mock_cleanup, mock_osclients):
|
||||
mock_app = mock.Mock(id="fake_app_id")
|
||||
(mock_osclients.Clients().murano().
|
||||
packages.create.return_value) = mock_app
|
||||
|
||||
murano_ctx = murano_packages.PackageGenerator(self._get_context())
|
||||
murano_ctx.setup()
|
||||
|
||||
murano_ctx.cleanup()
|
||||
mock_cleanup.assert_called_once_with(names=["murano.packages"],
|
||||
users=murano_ctx.context["users"])
|
146
tests/unit/plugins/openstack/context/network/test_allow_ssh.py
Normal file
146
tests/unit/plugins/openstack/context/network/test_allow_ssh.py
Normal file
@ -0,0 +1,146 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.network import allow_ssh
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
|
||||
CTX = "rally.plugins.openstack.context.network.allow_ssh"
|
||||
|
||||
|
||||
class AllowSSHContextTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(AllowSSHContextTestCase, self).setUp()
|
||||
self.users = 2
|
||||
task = {"uuid": "foo_task_id"}
|
||||
self.secgroup_name = allow_ssh.SSH_GROUP_NAME + "_foo"
|
||||
self.ctx_with_secgroup = {
|
||||
"users": [
|
||||
{
|
||||
"tenant_id": "uuid1",
|
||||
"endpoint": "endpoint",
|
||||
"secgroup": {"id": "secgroup_id", "name": "secgroup"}
|
||||
}
|
||||
] * self.users,
|
||||
"admin": {"tenant_id": "uuid2", "endpoint": "admin_endpoint"},
|
||||
"tenants": {"uuid1": {"id": "uuid1", "name": "uuid1"}},
|
||||
"task": task
|
||||
}
|
||||
self.ctx_without_secgroup = {
|
||||
"users": [{"tenant_id": "uuid1",
|
||||
"endpoint": "endpoint"},
|
||||
{"tenant_id": "uuid1",
|
||||
"endpoint": "endpoint"}],
|
||||
"admin": {"tenant_id": "uuid2", "endpoint": "admin_endpoint"},
|
||||
"tenants": {"uuid1": {"id": "uuid1", "name": "uuid1"}},
|
||||
"task": task
|
||||
}
|
||||
|
||||
@mock.patch("%s.osclients.Clients" % CTX)
|
||||
def test__prepare_open_secgroup(self, mock_clients):
|
||||
fake_nova = fakes.FakeNovaClient()
|
||||
self.assertEqual(len(fake_nova.security_groups.list()), 1)
|
||||
mock_cl = mock.MagicMock()
|
||||
mock_cl.nova.return_value = fake_nova
|
||||
mock_clients.return_value = mock_cl
|
||||
|
||||
ret = allow_ssh._prepare_open_secgroup("endpoint", self.secgroup_name)
|
||||
self.assertEqual(self.secgroup_name, ret["name"])
|
||||
|
||||
self.assertEqual(2, len(fake_nova.security_groups.list()))
|
||||
self.assertIn(
|
||||
self.secgroup_name,
|
||||
[sg.name for sg in fake_nova.security_groups.list()])
|
||||
|
||||
# run prep again, check that another security group is not created
|
||||
allow_ssh._prepare_open_secgroup("endpoint", self.secgroup_name)
|
||||
self.assertEqual(2, len(fake_nova.security_groups.list()))
|
||||
|
||||
@mock.patch("%s.osclients.Clients" % CTX)
|
||||
def test__prepare_open_secgroup_rules(self, mock_clients):
|
||||
fake_nova = fakes.FakeNovaClient()
|
||||
|
||||
# NOTE(hughsaunders) Default security group is precreated
|
||||
self.assertEqual(1, len(fake_nova.security_groups.list()))
|
||||
mock_cl = mock.MagicMock()
|
||||
mock_cl.nova.return_value = fake_nova
|
||||
mock_clients.return_value = mock_cl
|
||||
|
||||
allow_ssh._prepare_open_secgroup("endpoint", self.secgroup_name)
|
||||
|
||||
self.assertEqual(2, len(fake_nova.security_groups.list()))
|
||||
rally_open = fake_nova.security_groups.find(self.secgroup_name)
|
||||
self.assertEqual(3, len(rally_open.rules))
|
||||
|
||||
# run prep again, check that extra rules are not created
|
||||
allow_ssh._prepare_open_secgroup("endpoint", self.secgroup_name)
|
||||
rally_open = fake_nova.security_groups.find(self.secgroup_name)
|
||||
self.assertEqual(3, len(rally_open.rules))
|
||||
|
||||
@mock.patch("%s.osclients.Clients" % CTX)
|
||||
@mock.patch("%s._prepare_open_secgroup" % CTX)
|
||||
@mock.patch("rally.plugins.openstack.wrappers.network.wrap")
|
||||
def test_secgroup_setup_cleanup_with_secgroup_supported(
|
||||
self, mock_network_wrap, mock__prepare_open_secgroup,
|
||||
mock_clients):
|
||||
mock_network_wrapper = mock.MagicMock()
|
||||
mock_network_wrapper.supports_security_group.return_value = (
|
||||
True, "")
|
||||
mock_network_wrap.return_value = mock_network_wrapper
|
||||
mock__prepare_open_secgroup.return_value = {
|
||||
"name": "secgroup",
|
||||
"id": "secgroup_id"}
|
||||
mock_clients.return_value = mock.MagicMock()
|
||||
|
||||
secgrp_ctx = allow_ssh.AllowSSH(self.ctx_without_secgroup)
|
||||
secgrp_ctx.setup()
|
||||
self.assertEqual(self.ctx_with_secgroup, secgrp_ctx.context)
|
||||
secgrp_ctx.cleanup()
|
||||
|
||||
self.assertEqual(
|
||||
[
|
||||
mock.call("admin_endpoint"),
|
||||
mock.call("endpoint"),
|
||||
mock.call().nova(),
|
||||
mock.call().nova().security_groups.get("secgroup_id"),
|
||||
mock.call().nova().security_groups.get().delete()
|
||||
],
|
||||
mock_clients.mock_calls)
|
||||
|
||||
mock_network_wrap.assert_called_once_with(
|
||||
mock_clients.return_value, {})
|
||||
|
||||
@mock.patch("%s.osclients.Clients" % CTX)
|
||||
@mock.patch("rally.plugins.openstack.wrappers.network.wrap")
|
||||
def test_secgroup_setup_with_secgroup_unsupported(
|
||||
self, mock_network_wrap, mock_clients):
|
||||
mock_network_wrapper = mock.MagicMock()
|
||||
mock_network_wrapper.supports_security_group.return_value = (
|
||||
False, "Not supported")
|
||||
mock_network_wrap.return_value = mock_network_wrapper
|
||||
mock_clients.return_value = mock.MagicMock()
|
||||
|
||||
secgrp_ctx = allow_ssh.AllowSSH(dict(self.ctx_without_secgroup))
|
||||
secgrp_ctx.setup()
|
||||
self.assertEqual(self.ctx_without_secgroup, secgrp_ctx.context)
|
||||
|
||||
mock_clients.assert_called_once_with("admin_endpoint")
|
||||
|
||||
mock_network_wrap.assert_called_once_with(
|
||||
mock_clients.return_value, {})
|
79
tests/unit/plugins/openstack/context/network/test_network.py
Normal file
79
tests/unit/plugins/openstack/context/network/test_network.py
Normal file
@ -0,0 +1,79 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
import netaddr
|
||||
|
||||
from rally.plugins.openstack.context.network import networks as network_context
|
||||
from tests.unit import test
|
||||
|
||||
NET = "rally.plugins.openstack.wrappers.network."
|
||||
|
||||
|
||||
class NetworkTestCase(test.TestCase):
|
||||
def get_context(self, **kwargs):
|
||||
return {"task": {"uuid": "foo_task"},
|
||||
"admin": {"endpoint": "foo_admin"},
|
||||
"config": {"network": kwargs},
|
||||
"tenants": {"foo_tenant": {"networks": [{"id": "foo_net"}]},
|
||||
"bar_tenant": {"networks": [{"id": "bar_net"}]}}}
|
||||
|
||||
def test_START_CIDR_DFLT(self):
|
||||
netaddr.IPNetwork(network_context.Network.DEFAULT_CONFIG["start_cidr"])
|
||||
|
||||
@mock.patch("rally.osclients.Clients")
|
||||
@mock.patch(NET + "wrap", return_value="foo_service")
|
||||
def test__init__(self, mock_wrap, mock_clients):
|
||||
context = network_context.Network(self.get_context())
|
||||
self.assertEqual(context.net_wrapper, "foo_service")
|
||||
self.assertEqual(context.config["networks_per_tenant"], 1)
|
||||
self.assertEqual(context.config["start_cidr"],
|
||||
network_context.Network.DEFAULT_CONFIG["start_cidr"])
|
||||
|
||||
context = network_context.Network(
|
||||
self.get_context(start_cidr="foo_cidr", networks_per_tenant=42))
|
||||
self.assertEqual(context.net_wrapper, "foo_service")
|
||||
self.assertEqual(context.config["networks_per_tenant"], 42)
|
||||
self.assertEqual(context.config["start_cidr"], "foo_cidr")
|
||||
|
||||
@mock.patch(NET + "wrap")
|
||||
@mock.patch("rally.plugins.openstack.context.network.networks.utils")
|
||||
@mock.patch("rally.osclients.Clients")
|
||||
def test_setup(self, mock_clients, mock_utils, mock_wrap):
|
||||
mock_utils.iterate_per_tenants.return_value = [
|
||||
("foo_user", "foo_tenant"),
|
||||
("bar_user", "bar_tenant")]
|
||||
mock_create = mock.Mock(side_effect=lambda t, **kw: t + "-net")
|
||||
mock_wrap.return_value = mock.Mock(create_network=mock_create)
|
||||
nets_per_tenant = 2
|
||||
net_context = network_context.Network(
|
||||
self.get_context(networks_per_tenant=nets_per_tenant))
|
||||
net_context.setup()
|
||||
expected_networks = [["bar_tenant-net"] * nets_per_tenant,
|
||||
["foo_tenant-net"] * nets_per_tenant]
|
||||
actual_networks = []
|
||||
for tenant_id, tenant_ctx in (
|
||||
sorted(net_context.context["tenants"].items())):
|
||||
actual_networks.append(tenant_ctx["networks"])
|
||||
self.assertEqual(expected_networks, actual_networks)
|
||||
|
||||
@mock.patch("rally.osclients.Clients")
|
||||
@mock.patch(NET + "wrap")
|
||||
def test_cleanup(self, mock_wrap, mock_clients):
|
||||
net_context = network_context.Network(self.get_context())
|
||||
net_context.cleanup()
|
||||
mock_wrap().delete_network.assert_has_calls(
|
||||
[mock.call({"id": "foo_net"}), mock.call({"id": "bar_net"})],
|
||||
any_order=True)
|
121
tests/unit/plugins/openstack/context/nova/test_flavors.py
Normal file
121
tests/unit/plugins/openstack/context/nova/test_flavors.py
Normal file
@ -0,0 +1,121 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
from rally.plugins.openstack.context.nova import flavors
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.nova"
|
||||
|
||||
|
||||
class FlavorsGeneratorTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FlavorsGeneratorTestCase, self).setUp()
|
||||
self.context = {
|
||||
"config": {
|
||||
"flavors": [{
|
||||
"name": "flavor_name",
|
||||
"ram": 2048,
|
||||
"disk": 10,
|
||||
"vcpus": 3,
|
||||
"ephemeral": 3,
|
||||
"swap": 5,
|
||||
"extra_specs": {
|
||||
"key": "value"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
}
|
||||
|
||||
@mock.patch("%s.flavors.osclients.Clients" % CTX)
|
||||
def test_setup(self, mock_clients):
|
||||
# Setup and mock
|
||||
mock_create = mock_clients().nova().flavors.create
|
||||
mock_create().to_dict.return_value = {"flavor_key": "flavor_value"}
|
||||
|
||||
# Run
|
||||
flavors_ctx = flavors.FlavorsGenerator(self.context)
|
||||
flavors_ctx.setup()
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(flavors_ctx.context["flavors"],
|
||||
{"flavor_name": {"flavor_key": "flavor_value"}})
|
||||
|
||||
mock_clients.assert_called_with(self.context["admin"]["endpoint"])
|
||||
|
||||
mock_create.assert_called_with(
|
||||
name="flavor_name", ram=2048, vcpus=3,
|
||||
disk=10, ephemeral=3, swap=5)
|
||||
mock_create().set_keys.assert_called_with({"key": "value"})
|
||||
mock_create().to_dict.assert_called_with()
|
||||
|
||||
@mock.patch("%s.flavors.osclients.Clients" % CTX)
|
||||
def test_setup_failexists(self, mock_clients):
|
||||
# Setup and mock
|
||||
new_context = copy.deepcopy(self.context)
|
||||
new_context["flavors"] = {}
|
||||
|
||||
mock_flavor_create = mock_clients().nova().flavors.create
|
||||
|
||||
exception = nova_exceptions.Conflict("conflict")
|
||||
mock_flavor_create.side_effect = exception
|
||||
|
||||
# Run
|
||||
flavors_ctx = flavors.FlavorsGenerator(self.context)
|
||||
flavors_ctx.setup()
|
||||
|
||||
# Assertions
|
||||
self.assertEqual(new_context, flavors_ctx.context)
|
||||
|
||||
mock_clients.assert_called_with(self.context["admin"]["endpoint"])
|
||||
|
||||
mock_flavor_create.assert_called_once_with(
|
||||
name="flavor_name", ram=2048, vcpus=3,
|
||||
disk=10, ephemeral=3, swap=5)
|
||||
|
||||
@mock.patch("%s.flavors.osclients.Clients" % CTX)
|
||||
def test_cleanup(self, mock_clients):
|
||||
# Setup and mock
|
||||
real_context = {
|
||||
"flavors": {
|
||||
"flavor_name": {
|
||||
"flavor_name": "flavor_name",
|
||||
"id": "flavor_name"
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
}
|
||||
|
||||
# Run
|
||||
flavors_ctx = flavors.FlavorsGenerator(real_context)
|
||||
flavors_ctx.cleanup()
|
||||
|
||||
# Assertions
|
||||
mock_clients.assert_called_with(real_context["admin"]["endpoint"])
|
||||
|
||||
mock_flavors_delete = mock_clients().nova().flavors.delete
|
||||
mock_flavors_delete.assert_called_with("flavor_name")
|
92
tests/unit/plugins/openstack/context/nova/test_keypairs.py
Normal file
92
tests/unit/plugins/openstack/context/nova/test_keypairs.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Copyright 2014: Rackspace UK
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.nova import keypairs
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.nova"
|
||||
|
||||
|
||||
class KeyPairContextTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(KeyPairContextTestCase, self).setUp()
|
||||
self.users = 2
|
||||
self.keypair_name = keypairs.Keypair.KEYPAIR_NAME + "_foo_task_id"
|
||||
|
||||
task = {"uuid": "foo_task_id"}
|
||||
self.ctx_with_keys = {
|
||||
"users": [
|
||||
{
|
||||
"keypair": {
|
||||
"id": "key_id",
|
||||
"key": "key",
|
||||
"name": self.keypair_name
|
||||
},
|
||||
"endpoint": "endpoint"
|
||||
},
|
||||
] * self.users,
|
||||
"task": task
|
||||
}
|
||||
self.ctx_without_keys = {
|
||||
"users": [{"endpoint": "endpoint"}] * self.users,
|
||||
"task": task
|
||||
}
|
||||
|
||||
@mock.patch("%s.keypairs.Keypair._generate_keypair" % CTX)
|
||||
def test_keypair_setup(self, mock_keypair__generate_keypair):
|
||||
mock_keypair__generate_keypair.side_effect = [
|
||||
{"id": "key_id", "key": "key", "name": self.keypair_name},
|
||||
{"id": "key_id", "key": "key", "name": self.keypair_name},
|
||||
]
|
||||
|
||||
keypair_ctx = keypairs.Keypair(self.ctx_without_keys)
|
||||
keypair_ctx.setup()
|
||||
self.assertEqual(self.ctx_with_keys, keypair_ctx.context)
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call("endpoint")] * 2,
|
||||
mock_keypair__generate_keypair.mock_calls)
|
||||
|
||||
@mock.patch("%s.keypairs.resource_manager.cleanup" % CTX)
|
||||
def test_keypair_cleanup(self, mock_cleanup):
|
||||
keypair_ctx = keypairs.Keypair(self.ctx_with_keys)
|
||||
keypair_ctx.cleanup()
|
||||
mock_cleanup.assert_called_once_with(names=["nova.keypairs"],
|
||||
users=self.ctx_with_keys["users"])
|
||||
|
||||
@mock.patch("rally.osclients.Clients")
|
||||
def test_keypair_generate(self, mock_clients):
|
||||
mock_keypairs = mock_clients.return_value.nova.return_value.keypairs
|
||||
mock_keypair = mock_keypairs.create.return_value
|
||||
mock_keypair.public_key = "public_key"
|
||||
mock_keypair.private_key = "private_key"
|
||||
mock_keypair.id = "key_id"
|
||||
keypair_ctx = keypairs.Keypair(self.ctx_without_keys)
|
||||
key = keypair_ctx._generate_keypair("endpoint")
|
||||
|
||||
self.assertEqual({
|
||||
"id": "key_id",
|
||||
"name": "rally_ssh_key_foo_task_id",
|
||||
"private": "private_key",
|
||||
"public": "public_key"
|
||||
}, key)
|
||||
|
||||
mock_clients.assert_has_calls([
|
||||
mock.call().nova().keypairs.delete("rally_ssh_key_foo_task_id"),
|
||||
mock.call().nova().keypairs.create("rally_ssh_key_foo_task_id"),
|
||||
])
|
162
tests/unit/plugins/openstack/context/nova/test_servers.py
Normal file
162
tests/unit/plugins/openstack/context/nova/test_servers.py
Normal file
@ -0,0 +1,162 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
|
||||
from rally.plugins.openstack.context.nova import servers
|
||||
from tests.unit import fakes
|
||||
from tests.unit import test
|
||||
|
||||
CTX = "rally.plugins.openstack.context.nova"
|
||||
SCN = "rally.plugins.openstack.scenarios"
|
||||
TYP = "rally.task.types"
|
||||
|
||||
|
||||
class ServerGeneratorTestCase(test.ScenarioTestCase):
|
||||
|
||||
def _gen_tenants(self, count):
|
||||
tenants = {}
|
||||
for id_ in range(count):
|
||||
tenants[str(id_)] = {"name": str(id_)}
|
||||
return tenants
|
||||
|
||||
def test_init(self):
|
||||
tenants_count = 2
|
||||
servers_per_tenant = 5
|
||||
context = {}
|
||||
context["task"] = mock.MagicMock()
|
||||
context["config"] = {
|
||||
"servers": {
|
||||
"servers_per_tenant": servers_per_tenant,
|
||||
}
|
||||
}
|
||||
context["tenants"] = self._gen_tenants(tenants_count)
|
||||
|
||||
inst = servers.ServerGenerator(context)
|
||||
self.assertEqual(context["config"]["servers"], inst.config)
|
||||
|
||||
@mock.patch("%s.nova.utils.NovaScenario._boot_servers" % SCN,
|
||||
return_value=[
|
||||
fakes.FakeServer(id="uuid"),
|
||||
fakes.FakeServer(id="uuid"),
|
||||
fakes.FakeServer(id="uuid"),
|
||||
fakes.FakeServer(id="uuid"),
|
||||
fakes.FakeServer(id="uuid")
|
||||
])
|
||||
@mock.patch("%s.ImageResourceType.transform" % TYP,
|
||||
return_value=mock.MagicMock())
|
||||
@mock.patch("%s.FlavorResourceType.transform" % TYP,
|
||||
return_value=mock.MagicMock())
|
||||
@mock.patch("%s.servers.osclients" % CTX, return_value=fakes.FakeClients())
|
||||
def test_setup(self, mock_osclients, mock_flavor_resource_type_transform,
|
||||
mock_image_resource_type_transform,
|
||||
mock_nova_scenario__boot_servers):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
servers_per_tenant = 5
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants.keys():
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": mock.MagicMock()})
|
||||
|
||||
real_context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 5,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"servers": {
|
||||
"servers_per_tenant": 5,
|
||||
"image": {
|
||||
"name": "cirros-0.3.2-x86_64-uec",
|
||||
},
|
||||
"flavor": {
|
||||
"name": "m1.tiny",
|
||||
},
|
||||
},
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
new_context = copy.deepcopy(real_context)
|
||||
for id_ in new_context["tenants"]:
|
||||
new_context["tenants"][id_].setdefault("servers", [])
|
||||
for i in range(servers_per_tenant):
|
||||
new_context["tenants"][id_]["servers"].append("uuid")
|
||||
|
||||
servers_ctx = servers.ServerGenerator(real_context)
|
||||
servers_ctx.setup()
|
||||
self.assertEqual(new_context, real_context)
|
||||
|
||||
@mock.patch("%s.servers.osclients" % CTX)
|
||||
@mock.patch("%s.servers.resource_manager.cleanup" % CTX)
|
||||
def test_cleanup(self, mock_cleanup, mock_osclients):
|
||||
|
||||
tenants_count = 2
|
||||
users_per_tenant = 5
|
||||
servers_per_tenant = 5
|
||||
|
||||
tenants = self._gen_tenants(tenants_count)
|
||||
users = []
|
||||
for id_ in tenants.keys():
|
||||
for i in range(users_per_tenant):
|
||||
users.append({"id": i, "tenant_id": id_,
|
||||
"endpoint": "endpoint"})
|
||||
tenants[id_].setdefault("servers", [])
|
||||
for j in range(servers_per_tenant):
|
||||
tenants[id_]["servers"].append("uuid")
|
||||
|
||||
context = {
|
||||
"config": {
|
||||
"users": {
|
||||
"tenants": 2,
|
||||
"users_per_tenant": 5,
|
||||
"concurrent": 10,
|
||||
},
|
||||
"servers": {
|
||||
"servers_per_tenant": 5,
|
||||
"image": {
|
||||
"name": "cirros-0.3.2-x86_64-uec",
|
||||
},
|
||||
"flavor": {
|
||||
"name": "m1.tiny",
|
||||
},
|
||||
},
|
||||
},
|
||||
"admin": {
|
||||
"endpoint": mock.MagicMock()
|
||||
},
|
||||
"task": mock.MagicMock(),
|
||||
"users": users,
|
||||
"tenants": tenants
|
||||
}
|
||||
|
||||
servers_ctx = servers.ServerGenerator(context)
|
||||
servers_ctx.cleanup()
|
||||
|
||||
mock_cleanup.assert_called_once_with(names=["nova.servers"],
|
||||
users=context["users"])
|
Loading…
x
Reference in New Issue
Block a user