Separate deployments from tasks

The patch introduce deployments as independent operations. The command
line interface supports of creation, deletion and recreation of
deployments. The creation of the task requires the id of a deployment.

The patch also separate samples of configuration to two types: a
deployment and a task.

Change-Id: I5121532057909040e6ab6d6b2cbd6d06812975cb
Implements: blueprint independent-deploy
This commit is contained in:
Ilya Kharin 2013-11-20 19:05:48 +04:00 committed by Gerrit Code Review
parent 03d24ea307
commit 18aa88bca7
11 changed files with 129 additions and 125 deletions

View File

@ -0,0 +1,7 @@
{
"name": "DevstackEngine",
"provider": {
"name": "DummyProvider",
"credentials": ["root@10.2.0.8"]
}
}

View File

@ -0,0 +1,12 @@
{
"name": "DummyEngine",
"cloud_config": {
"identity": {
"url": "http://example.net/",
"uri": "http://example.net:5000/v2.0/",
"admin_username": "admin",
"admin_password": "myadminpass",
"admin_tenant_name": "demo"
}
}
}

View File

@ -1,27 +1,12 @@
{
"deploy": {
"name": "DummyEngine",
"cloud_config": {
"identity": {
"url": "http://example.net/",
"uri": "http://example.net:5000/v2.0/",
"admin_username": "admin",
"admin_password": "myadminpass",
"admin_tenant_name": "demo"
}
}
},
"tests": {
"verify": [],
"benchmark": {
"NovaServers.boot_and_delete_server": [
{"args": {"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979"},
"execution": "continuous",
"config": {"times": 10, "active_users": 2,
"tenants": 3, "users_per_tenant": 2}}
]
}
"verify": [],
"benchmark": {
"NovaServers.boot_and_delete_server": [
{"args": {"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979"},
"execution": "continuous",
"config": {"times": 10, "active_users": 2,
"tenants": 3, "users_per_tenant": 2}}
]
}
}

View File

@ -1,22 +1,7 @@
{
"deploy": {
"name": "DummyEngine",
"cloud_config": {
"identity": {
"url": "http://example.net/",
"uri": "http://example.net:5000/v2.0/",
"admin_username": "admin",
"admin_password": "myadminpass",
"admin_tenant_name": "demo"
}
}
},
"tests": {
"verify": [
"sanity",
"smoke"
],
"benchmark": {}
}
"verify": [
"sanity",
"smoke"
],
"benchmark": {}
}

View File

@ -1,18 +1,4 @@
{
"deploy": {
"name": "DummyEngine",
"cloud_config": {
"identity": {
"url": "http://example.net/",
"uri": "http://example.net:5000/v2.0/",
"admin_username": "admin",
"admin_password": "myadminpass",
"admin_tenant_name": "demo"
}
}
},
"tests": {
"verify": [],
"benchmark": {
"NovaServers.boot_and_bounce_server": [
@ -22,5 +8,4 @@
"config": {"times": 3, "active_users": 2}}
]
}
}
}

View File

@ -105,18 +105,19 @@ class DeploymentCommands(object):
class TaskCommands(object):
# TODO(akscram): We should to specify an UUID of the deployment via
# the --deploy-id argument.
@cliutils.args('--deploy-id', type=str, dest='deploy_id', required=True,
help='UUID of the deployment')
@cliutils.args('--task',
help='Path to the file with full configuration of task')
def start(self, task):
"""Run Benchmark task
:param config: File with json configration
Returns task_uuid
def start(self, deploy_id, task):
"""Run a benchmark task.
:param deploy_id: an UUID of a deployment
:param config: a file with json configration
"""
with open(task) as task_file:
config_dict = json.load(task_file)
api.start_task(config_dict)
api.start_task(deploy_id, config_dict)
@cliutils.args('--task-id', type=str, dest='task_id', help='UUID of task')
def abort(self, task_id):

View File

@ -126,9 +126,7 @@ class Task(BASE, RallyBase):
deployment_uuid = sa.Column(
sa.String(36),
sa.ForeignKey(Deployment.uuid),
# TODO(akscram): We should to set nullable=False after
# separation. It's True only for compatibility.
nullable=True,
nullable=False,
)
deployment = sa.orm.relationship(
Deployment,

View File

@ -26,7 +26,12 @@ def create_deploy(config, name):
:param config: a dict with deployment configuration
:param name: a str represents a name of the deployment
"""
raise NotImplementedError()
deployment = objects.Deployment(name=name, config=config)
deployer = deploy.EngineFactory.get_engine(deployment['config']['name'],
deployment)
with deployer:
endpoint = deployer.make_deploy()
deployment.update_endpoint(endpoint)
def destroy_deploy(deploy_uuid):
@ -34,7 +39,16 @@ def destroy_deploy(deploy_uuid):
:param deploy_uuid: UUID of the deployment
"""
raise NotImplementedError()
# TODO(akscram): We have to be sure that there are no running
# tasks for this deployment.
# TODO(akscram): Check that the deployment have got a status that
# is equal to "*->finised" or "deploy->inconsistent".
deployment = objects.Deployment.get(deploy_uuid)
deployer = deploy.EngineFactory.get_engine(deployment['config']['name'],
deployment)
with deployer:
deployer.make_cleanup()
deployment.delete()
def recreate_deploy(deploy_uuid):
@ -42,38 +56,37 @@ def recreate_deploy(deploy_uuid):
:param deploy_uuid: UUID of the deployment
"""
raise NotImplementedError()
# TODO(akscram): The additional argument with the UUID of the
# deployment.
def start_task(config):
"""Start Benchmark task.
1) Deploy OpenStack Cloud
2) Verify Deployment
3) Run Benchmarks
4) Process benchmark results
5) Destroy cloud and cleanup
Returns task uuid
"""
deploy_conf = config['deploy']
benchmark_conf = config['tests']
deployment = objects.Deployment(config=deploy_conf)
task = objects.Task(deployment_uuid=deployment['uuid'])
deployment = objects.Deployment.get(deploy_uuid)
deployer = deploy.EngineFactory.get_engine(deployment['config']['name'],
deployment)
tester = engine.TestEngine(benchmark_conf, task)
with deployer:
deployer.make_cleanup()
endpoint = deployer.make_deploy()
deployment.update_endpoint(endpoint)
def start_task(deploy_uuid, config):
"""Start a task.
A task is performed in two stages: a verification of a deployment
and a benchmark.
:param deploy_uuid: UUID of the deployment
:param config: a dict with a task configuration
"""
deployment = objects.Deployment.get(deploy_uuid)
task = objects.Task(deployment_uuid=deploy_uuid)
tester = engine.TestEngine(config, task)
deployer = deploy.EngineFactory.get_engine(deployment['config']['name'],
deployment)
endpoint = deployment['endpoint']
with deployer:
with tester.bind(endpoint):
# TODO(akscram): The verifications should be a part of
# deployment.
tester.verify()
tester.benchmark()
deployer.make_cleanup()
# TODO(akscram): It's just to follow legacy logic.
deployment.delete()
def abort_task(task_uuid):

View File

@ -31,8 +31,9 @@ class TaskCommandsTestCase(test.BaseTestCase):
mock.mock_open(read_data='{"some": "json"}'),
create=True)
def test_start(self, mock_api):
self.task.start('path_to_config.json')
mock_api.assert_called_once_with({'some': 'json'})
deploy_id = str(uuid.uuid4())
self.task.start(deploy_id, 'path_to_config.json')
mock_api.assert_called_once_with(deploy_id, {'some': 'json'})
def test_abort(self):
test_uuid = str(uuid.uuid4())

View File

@ -24,12 +24,18 @@ from rally import test
class TasksTestCase(test.DBTestCase):
def setUp(self):
super(TasksTestCase, self).setUp()
self.deploy = db.deployment_create({})
def _get_task(self, uuid):
return db.task_get(uuid)
def _create_task(self, values=None):
return db.task_create(values or {})
values = values or {}
if 'deployment_uuid' not in values:
values['deployment_uuid'] = self.deploy['uuid']
return db.task_create(values)
def test_task_get_not_found(self):
self.assertRaises(exceptions.TaskNotFound,

View File

@ -72,6 +72,7 @@ class APITestCase(test.TestCase):
def setUp(self):
super(APITestCase, self).setUp()
self.deploy_config = FAKE_DEPLOY_CONFIG
self.task_config = FAKE_TASK_CONFIG
self.deploy_uuid = str(uuid.uuid4())
self.endpoint = FAKE_DEPLOY_CONFIG['cloud_config']
self.task_uuid = str(uuid.uuid4())
@ -84,27 +85,19 @@ class APITestCase(test.TestCase):
'config': self.deploy_config,
'endpoint': self.endpoint,
}
self.full_config = {
'deploy': FAKE_DEPLOY_CONFIG,
'tests': FAKE_TASK_CONFIG,
}
@mock.patch('rally.benchmark.engine.utils.ScenarioRunner')
@mock.patch('rally.benchmark.engine.utils.Verifier')
@mock.patch('rally.objects.deploy.db.deployment_get')
@mock.patch('rally.objects.task.db.task_result_create')
@mock.patch('rally.objects.deploy.db.deployment_delete')
@mock.patch('rally.objects.deploy.db.deployment_create')
@mock.patch('rally.objects.deploy.db.deployment_update')
@mock.patch('rally.objects.task.db.task_update')
@mock.patch('rally.objects.task.db.task_create')
def test_start_task(self, mock_task_create, mock_task_update,
mock_deploy_update, mock_deploy_create,
mock_deploy_delete, mock_task_result_create,
mock_task_result_create, mock_deploy_get,
mock_utils_verifier, mock_utils_runner):
mock_task_create.return_value = self.task
mock_task_update.return_value = self.task
mock_deploy_create.return_value = self.deployment
mock_deploy_update.return_value = self.deployment
mock_deploy_get.return_value = self.deployment
mock_utils_verifier.return_value = mock_verifier = mock.Mock()
mock_utils_verifier.list_verification_tests.return_value = {
@ -117,15 +110,9 @@ class APITestCase(test.TestCase):
mock_utils_runner.return_value = mock_runner = mock.Mock()
mock_runner.run.return_value = ['fake_result']
api.start_task(self.full_config)
api.start_task(self.deploy_uuid, self.task_config)
mock_deploy_create.assert_called_once_with(
{'config': self.deploy_config})
mock_deploy_update.assert_has_calls([
mock.call(self.deploy_uuid, {'status': 'deploy->started'}),
mock.call(self.deploy_uuid, {'status': 'deploy->finished'}),
mock.call(self.deploy_uuid, {'endpoint': self.endpoint}),
])
mock_deploy_get.assert_called_once_with(self.deploy_uuid)
mock_task_create.assert_called_once_with({
'deployment_uuid': self.deploy_uuid,
})
@ -159,8 +146,6 @@ class APITestCase(test.TestCase):
'raw': ['fake_result'],
},
)
# TODO(akscram): It's just to follow legacy logic.
mock_deploy_delete.assert_called_once_with(self.deploy_uuid)
def test_abort_task(self):
self.assertRaises(NotImplementedError, api.abort_task,
@ -178,11 +163,37 @@ class APITestCase(test.TestCase):
api.delete_task(self.task_uuid, force=True)
mock_delete.assert_called_once_with(self.task_uuid, status=None)
def test_create_deploy(self):
self.assertRaises(NotImplementedError, api.create_deploy, 'name', {})
@mock.patch('rally.objects.deploy.db.deployment_update')
@mock.patch('rally.objects.deploy.db.deployment_create')
def test_create_deploy(self, mock_create, mock_update):
mock_create.return_value = self.deployment
mock_update.return_value = self.deployment
api.create_deploy(self.deploy_config, 'fake_deploy')
mock_create.assert_called_once_with({
'name': 'fake_deploy',
'config': self.deploy_config,
})
mock_update.assert_has_calls([
mock.call(self.deploy_uuid, {'endpoint': self.endpoint}),
])
def test_destroy_deploy(self):
self.assertRaises(NotImplementedError, api.destroy_deploy, 'uuid')
@mock.patch('rally.objects.deploy.db.deployment_delete')
@mock.patch('rally.objects.deploy.db.deployment_update')
@mock.patch('rally.objects.deploy.db.deployment_get')
def test_destroy_deploy(self, mock_get, mock_update, mock_delete):
mock_get.return_value = self.deployment
mock_update.return_value = self.deployment
api.destroy_deploy(self.deploy_uuid)
mock_get.assert_called_once_with(self.deploy_uuid)
mock_delete.assert_called_once_with(self.deploy_uuid)
def test_recreate_deploy(self):
self.assertRaises(NotImplementedError, api.recreate_deploy, 'uuid')
@mock.patch('rally.objects.deploy.db.deployment_update')
@mock.patch('rally.objects.deploy.db.deployment_get')
def test_recreate_deploy(self, mock_get, mock_update):
mock_get.return_value = self.deployment
mock_update.return_value = self.deployment
api.recreate_deploy(self.deploy_uuid)
mock_get.assert_called_once_with(self.deploy_uuid)
mock_update.assert_has_calls([
mock.call(self.deploy_uuid, {'endpoint': self.endpoint}),
])