Merge "Add context that creates servers using EC2 api"

This commit is contained in:
Jenkins 2015-08-11 16:04:08 +00:00 committed by Gerrit Code Review
commit 8b2c7a3803
8 changed files with 260 additions and 36 deletions

View File

@ -0,0 +1,98 @@
# 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.ec2 import utils as ec2_utils
from rally.task import context
from rally.task import types
LOG = logging.getLogger(__name__)
@context.configure(name="ec2_servers", order=460)
class EC2ServerGenerator(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", "servers_per_tenant"],
"additionalProperties": False
}
@rutils.log_task_wrapper(LOG.info, _("Enter context: `EC2 Servers`"))
def setup(self):
image = self.config["image"]
flavor = self.config["flavor"]
clients = osclients.Clients(self.context["users"][0]["endpoint"])
image_id = types.EC2ImageResourceType.transform(clients=clients,
resource_config=image)
for user, tenant_id in rutils.iterate_per_tenants(
self.context["users"]):
LOG.debug("Booting servers for tenant %s "
% (user["tenant_id"]))
user_clients = osclients.Clients(user["endpoint"])
ec2_scenario = ec2_utils.EC2Scenario(clients=user_clients)
LOG.debug(
"Calling _boot_servers with "
"image_id={image_id} flavor_name={flavor_name} "
"servers_per_tenant={servers_per_tenant}".format(
image_id=image_id, flavor_name=flavor["name"],
servers_per_tenant=self.config["servers_per_tenant"]))
servers = ec2_scenario._boot_servers(
image_id, flavor["name"], self.config["servers_per_tenant"])
current_servers = [server.id for server in servers]
self.context["tenants"][tenant_id]["ec2_servers"] = current_servers
@rutils.log_task_wrapper(LOG.info, _("Exit context: `EC2 Servers`"))
def cleanup(self):
resource_manager.cleanup(names=["ec2.servers"],
users=self.context.get("users", []))

View File

@ -41,4 +41,4 @@ class EC2Servers(utils.EC2Scenario):
:param flavor: flavor to be used to boot an instance
:param kwargs: optional additional arguments for server creation
"""
self._boot_server(image, flavor, **kwargs)
self._boot_servers(image, flavor, **kwargs)

View File

@ -48,33 +48,38 @@ CONF.register_opts(EC2_BENCHMARK_OPTS, group=benchmark_group)
class EC2Scenario(scenario.OpenStackScenario):
"""Base class for EC2 scenarios with basic atomic actions."""
RESOURCE_NAME_PREFIX = "rally_ec2server_"
RESOURCE_NAME_LENGTH = 16
@base.atomic_action_timer("ec2.boot_servers")
def _boot_servers(self, image_id, flavor_name,
instance_num=1, **kwargs):
"""Boot multiple servers.
@base.atomic_action_timer("ec2.boot_server")
def _boot_server(self, image_id, flavor_name, **kwargs):
"""Boot a server.
Returns when the server is actually booted and in "Running" state.
Returns when all the servers are actually booted and are in the
"Running" state.
:param image_id: ID of the image to be used for server creation
:param flavor_name: Name of the flavor to be used for server creation
:param kwargs: other optional parameters to initialize the server
:returns: EC2 Server instance
:param instance_num: Number of instances to boot
:param kwargs: Other optional parameters to boot servers
:returns: List of created server objects
"""
reservation = self.clients("ec2").run_instances(
image_id=image_id, instance_type=flavor_name, **kwargs)
server = reservation.instances[0]
image_id=image_id,
instance_type=flavor_name,
min_count=instance_num,
max_count=instance_num,
**kwargs)
servers = [instance for instance in reservation.instances]
time.sleep(CONF.benchmark.ec2_server_boot_prepoll_delay)
server = utils.wait_for(
servers = [utils.wait_for(
server,
is_ready=utils.resource_is("RUNNING"),
update_resource=self._update_resource,
timeout=CONF.benchmark.ec2_server_boot_timeout,
check_interval=CONF.benchmark.ec2_server_boot_poll_interval
)
return server
) for server in servers]
return servers
def _update_resource(self, resource):
resource.update()

View File

@ -0,0 +1,110 @@
# 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.ec2 import servers
from tests.unit import fakes
from tests.unit import test
CTX = "rally.plugins.openstack.context.ec2"
SCN = "rally.plugins.openstack.scenarios"
TYP = "rally.task.types"
class EC2ServerGeneratorTestCase(test.TestCase):
def _gen_tenants_and_users(self, tenants_count, users_per_tenant):
tenants = {}
for id in range(tenants_count):
tenants[str(id)] = dict(name=str(id))
users = []
for tenant_id in tenants.keys():
for i in range(users_per_tenant):
users.append({"id": i, "tenant_id": tenant_id,
"endpoint": "endpoint"})
return tenants, users
def _get_context(self, users, tenants):
return {
"config": {
"users": {
"tenants": 2,
"users_per_tenant": 5,
"concurrent": 10},
"ec2_servers": {
"servers_per_tenant": 5,
"image": {"name": "foo_image"},
"flavor": {"name": "foo_flavor"}
}
},
"admin": {"endpoint": mock.MagicMock()},
"task": mock.MagicMock(),
"users": users,
"tenants": tenants
}
@mock.patch("%s.ec2.utils.EC2Scenario._boot_servers" % SCN,
return_value=[fakes.FakeServer(id=str(i)) for i in range(5)])
@mock.patch("%s.EC2ImageResourceType.transform" % TYP,
return_value=mock.MagicMock())
@mock.patch("%s.servers.osclients" % CTX, return_value=fakes.FakeClients())
def test_setup(self, mock_osclients,
mock_ec2_image_resource_type_transform,
mock_ec2_scenario__boot_servers):
tenants_count = 2
users_per_tenant = 5
servers_per_tenant = 5
tenants, users = self._gen_tenants_and_users(tenants_count,
users_per_tenant)
real_context = self._get_context(users, tenants)
new_context = copy.deepcopy(real_context)
for tenant_id in new_context["tenants"]:
new_context["tenants"][tenant_id].setdefault("ec2_servers", [])
for i in range(servers_per_tenant):
new_context["tenants"][tenant_id]["ec2_servers"].append(str(i))
servers_ctx = servers.EC2ServerGenerator(real_context)
servers_ctx.setup()
self.assertEqual(new_context, servers_ctx.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, users = self._gen_tenants_and_users(tenants_count,
users_per_tenant)
for tenant_id in tenants.keys():
tenants[tenant_id].setdefault("ec2_servers", [])
for i in range(servers_per_tenant):
tenants[tenant_id]["ec2_servers"].append(str(i))
context = self._get_context(users, tenants)
servers_ctx = servers.EC2ServerGenerator(context)
servers_ctx.cleanup()
mock_cleanup.assert_called_once_with(names=["ec2.servers"],
users=context["users"])

View File

@ -22,7 +22,7 @@ class EC2ServersTestCase(test.ScenarioTestCase):
def test_boot_server(self):
scenario = servers.EC2Servers()
scenario._boot_server = mock.Mock()
scenario._boot_servers = mock.Mock()
scenario.boot_server("foo_image", "foo_flavor", foo="bar")
scenario._boot_server.assert_called_once_with(
scenario._boot_servers.assert_called_once_with(
"foo_image", "foo_flavor", foo="bar")

View File

@ -18,7 +18,6 @@ from oslo_config import cfg
from rally.plugins.openstack.scenarios.ec2 import utils
from tests.unit import test
EC2_UTILS = "rally.plugins.openstack.scenarios.ec2.utils"
CONF = cfg.CONF
@ -26,27 +25,39 @@ class EC2ScenarioTestCase(test.ScenarioTestCase):
def setUp(self):
super(EC2ScenarioTestCase, self).setUp()
self.server = mock.MagicMock()
self.reservation = mock.MagicMock(instances=[self.server])
def test__boot_server(self):
self.clients("ec2").run_instances.return_value = self.reservation
ec2_scenario = utils.EC2Scenario(context={})
ec2_scenario._update_resource = mock.Mock()
return_server = ec2_scenario._boot_server("image", "flavor")
self.mock_wait_for.mock.assert_called_once_with(
self.server,
is_ready=self.mock_resource_is.mock.return_value,
update_resource=ec2_scenario._update_resource,
check_interval=CONF.benchmark.ec2_server_boot_poll_interval,
timeout=CONF.benchmark.ec2_server_boot_timeout)
self.mock_resource_is.mock.assert_called_once_with("RUNNING")
self.assertEqual(self.mock_wait_for.mock.return_value, return_server)
self._test_atomic_action_timer(ec2_scenario.atomic_actions(),
"ec2.boot_server")
self.server1 = mock.MagicMock()
self.server2 = mock.MagicMock()
self.reservations = mock.MagicMock(instances=[self.server1,
self.server2])
def test__update_resource(self):
resource = mock.MagicMock()
scenario = utils.EC2Scenario()
self.assertEqual(scenario._update_resource(resource), resource)
resource.update.assert_called_once_with()
def test__boot_servers(self):
self.clients("ec2").run_instances.return_value = self.reservations
ec2_scenario = utils.EC2Scenario(context={})
ec2_scenario._update_resource = mock.Mock()
ec2_scenario._boot_servers("image", "flavor", 2)
expected = [
mock.call(
self.server1,
is_ready=self.mock_resource_is.mock.return_value,
update_resource=ec2_scenario._update_resource,
check_interval=CONF.benchmark.ec2_server_boot_poll_interval,
timeout=CONF.benchmark.ec2_server_boot_timeout
),
mock.call(
self.server2,
is_ready=self.mock_resource_is.mock.return_value,
update_resource=ec2_scenario._update_resource,
check_interval=CONF.benchmark.ec2_server_boot_poll_interval,
timeout=CONF.benchmark.ec2_server_boot_timeout
)
]
self.mock_wait_for.mock.assert_has_calls(expected)
self.mock_resource_is.mock.assert_has_calls([mock.call("RUNNING")])
self._test_atomic_action_timer(ec2_scenario.atomic_actions(),
"ec2.boot_servers")