From c83fa7375e759f8aed9c5f5d6f54ccafdc2de42e Mon Sep 17 00:00:00 2001 From: Ilya Shakhat Date: Tue, 10 Mar 2015 15:53:24 +0300 Subject: [PATCH] Refactor agents processing and add unit tests Change-Id: I810f7ea8b5d4316c7813552a7fba7703337a4f53 --- scenarios/networking/l2.hot | 55 ++---- scenarios/networking/l3_east_west.hot | 58 +++--- scenarios/networking/l3_north_south.hot | 62 +++---- scenarios/storage/dummy.hot | 24 +-- shaker/engine/deploy.py | 170 +++++++++--------- shaker/engine/server.py | 72 ++++---- shaker/engine/utils.py | 4 + test-requirements.txt | 1 + tests/test_deploy.py | 227 ++++++++++++++++++++++++ tests/test_server.py | 88 +++++++++ 10 files changed, 513 insertions(+), 248 deletions(-) create mode 100644 tests/test_deploy.py create mode 100644 tests/test_server.py diff --git a/scenarios/networking/l2.hot b/scenarios/networking/l2.hot index cbaa1b4..5240bca 100644 --- a/scenarios/networking/l2.hot +++ b/scenarios/networking/l2.hot @@ -58,17 +58,17 @@ resources: {remote_ip_prefix: 0.0.0.0/0, protocol: icmp}] -{% for group in groups %} +{% for agent in agents.values() %} - {{ group.master.name }}: + {{ agent.id }}: type: OS::Nova::Server properties: - name: {{ group.master.name }} + name: {{ agent.id }} image: { get_param: image } flavor: { get_param: flavor } - availability_zone: "nova:{{ group.master.node }}" + availability_zone: "nova:{{ agent.node }}" networks: - - port: { get_resource: {{ group.master.name }}_port } + - port: { get_resource: {{ agent.id }}_port } user_data_format: RAW user_data: str_replace: @@ -77,36 +77,9 @@ resources: screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID params: "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.master.name }} + "$AGENT_ID": {{ agent.id }} - {{ group.master.name }}_port: - type: OS::Neutron::Port - properties: - network_id: { get_resource: private_net } - fixed_ips: - - subnet_id: { get_resource: private_subnet } - security_groups: [{ get_resource: server_security_group }] - - {{ group.slave.name }}: - type: OS::Nova::Server - properties: - name: {{ group.slave.name }} - image: { get_param: image } - flavor: { get_param: flavor } - availability_zone: "nova:{{ group.slave.node }}" - networks: - - port: { get_resource: {{ group.slave.name }}_port } - user_data_format: RAW - user_data: - str_replace: - template: | - #!/bin/sh - screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID - params: - "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.slave.name }} - - {{ group.slave.name }}_port: + {{ agent.id }}_port: type: OS::Neutron::Port properties: network_id: { get_resource: private_net } @@ -117,13 +90,9 @@ resources: {% endfor %} outputs: -{% for group in groups %} - {{ group.master.name }}_ip: - value: { get_attr: [ {{ group.master.name }}, networks, { get_attr: [private_net, name] }, 0 ] } - {{ group.master.name }}_instance_name: - value: { get_attr: [ {{ group.master.name }}, instance_name ] } - {{ group.slave.name }}_ip: - value: { get_attr: [ {{ group.slave.name }}, networks, { get_attr: [private_net, name] }, 0 ] } - {{ group.slave.name }}_instance_name: - value: { get_attr: [ {{ group.slave.name }}, instance_name ] } +{% for agent in agents.values() %} + {{ agent.id }}_instance_name: + value: { get_attr: [ {{ agent.id }}, instance_name ] } + {{ agent.id }}_ip: + value: { get_attr: [ {{ agent.id }}, networks, { get_attr: [private_net, name] }, 0 ] } {% endfor %} diff --git a/scenarios/networking/l3_east_west.hot b/scenarios/networking/l3_east_west.hot index 96b4f85..bf7a7b2 100644 --- a/scenarios/networking/l3_east_west.hot +++ b/scenarios/networking/l3_east_west.hot @@ -76,17 +76,17 @@ resources: {remote_ip_prefix: 0.0.0.0/0, protocol: icmp}] -{% for group in groups %} +{% for agent in agents.values() %} - {{ group.master.name }}: + {{ agent.id }}: type: OS::Nova::Server properties: - name: {{ group.master.name }} + name: {{ agent.id }} image: { get_param: image } flavor: { get_param: flavor } - availability_zone: "nova:{{ group.master.node }}" + availability_zone: "nova:{{ agent.node }}" networks: - - port: { get_resource: {{ group.master.name }}_port } + - port: { get_resource: {{ agent.id }}_port } user_data_format: RAW user_data: str_replace: @@ -95,53 +95,37 @@ resources: screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID params: "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.master.name }} + "$AGENT_ID": {{ agent.id }} - {{ group.master.name }}_port: +{% if agent.mode == 'master' %} + {{ agent.id }}_port: type: OS::Neutron::Port properties: network_id: { get_resource: east_private_net } fixed_ips: - subnet_id: { get_resource: east_private_subnet } security_groups: [{ get_resource: server_security_group }] - - {{ group.slave.name }}: - type: OS::Nova::Server - properties: - name: {{ group.slave.name }} - image: { get_param: image } - flavor: { get_param: flavor } - availability_zone: "nova:{{ group.slave.node }}" - networks: - - port: { get_resource: {{ group.slave.name }}_port } - user_data_format: RAW - user_data: - str_replace: - template: | - #!/bin/sh - screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID - params: - "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.slave.name }} - - {{ group.slave.name }}_port: +{% else %} + {{ agent.id }}_port: type: OS::Neutron::Port properties: network_id: { get_resource: west_private_net } fixed_ips: - subnet_id: { get_resource: west_private_subnet } security_groups: [{ get_resource: server_security_group }] +{% endif %} {% endfor %} outputs: -{% for group in groups %} - {{ group.master.name }}_ip: - value: { get_attr: [ {{ group.master.name }}, networks, { get_attr: [east_private_net, name] }, 0 ] } - {{ group.master.name }}_instance_name: - value: { get_attr: [ {{ group.master.name }}, instance_name ] } - {{ group.slave.name }}_ip: - value: { get_attr: [ {{ group.slave.name }}, networks, { get_attr: [west_private_net, name] }, 0 ] } - {{ group.slave.name }}_instance_name: - value: { get_attr: [ {{ group.slave.name }}, instance_name ] } +{% for agent in agents.values() %} + {{ agent.id }}_instance_name: + value: { get_attr: [ {{ agent.id }}, instance_name ] } +{% if agent.mode == 'master' %} + {{ agent.id }}_ip: + value: { get_attr: [ {{ agent.id }}, networks, { get_attr: [east_private_net, name] }, 0 ] } +{% else %} + {{ agent.id }}_ip: + value: { get_attr: [ {{ agent.id }}, networks, { get_attr: [west_private_net, name] }, 0 ] } +{% endif %} {% endfor %} diff --git a/scenarios/networking/l3_north_south.hot b/scenarios/networking/l3_north_south.hot index 84eb90b..f33aab9 100644 --- a/scenarios/networking/l3_north_south.hot +++ b/scenarios/networking/l3_north_south.hot @@ -84,17 +84,17 @@ resources: {remote_ip_prefix: 0.0.0.0/0, protocol: icmp}] -{% for group in groups %} +{% for agent in agents.values() %} - {{ group.master.name }}: + {{ agent.id }}: type: OS::Nova::Server properties: - name: {{ group.master.name }} + name: {{ agent.id }} image: { get_param: image } flavor: { get_param: flavor } - availability_zone: "nova:{{ group.master.node }}" + availability_zone: "nova:{{ agent.node }}" networks: - - port: { get_resource: {{ group.master.name }}_port } + - port: { get_resource: {{ agent.id }}_port } user_data_format: RAW user_data: str_replace: @@ -103,36 +103,18 @@ resources: screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID params: "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.slave.name }} + "$AGENT_ID": {{ agent.id }} - {{ group.master.name }}_port: +{% if agent.mode == 'master' %} + {{ agent.id }}_port: type: OS::Neutron::Port properties: network_id: { get_resource: north_private_net } fixed_ips: - subnet_id: { get_resource: north_private_subnet } security_groups: [{ get_resource: server_security_group }] - - {{ group.slave.name }}: - type: OS::Nova::Server - properties: - name: {{ group.slave.name }} - image: { get_param: image } - flavor: { get_param: flavor } - availability_zone: "nova:{{ group.slave.node }}" - networks: - - port: { get_resource: {{ group.slave.name }}_port } - user_data_format: RAW - user_data: - str_replace: - template: | - #!/bin/sh - screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID - params: - "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.slave.name }} - - {{ group.slave.name }}_port: +{% else %} + {{ agent.id }}_port: type: OS::Neutron::Port properties: network_id: { get_resource: south_private_net } @@ -140,22 +122,24 @@ resources: - subnet_id: { get_resource: south_private_subnet } security_groups: [{ get_resource: server_security_group }] - {{ group.slave.name }}_floating_ip: + {{ agent.id }}_floating_ip: type: OS::Neutron::FloatingIP properties: floating_network: { get_param: external_net } - port_id: { get_resource: {{ group.slave.name }}_port } + port_id: { get_resource: {{ agent.id }}_port } +{% endif %} {% endfor %} outputs: -{% for group in groups %} - {{ group.master.name }}_ip: - value: { get_attr: [ {{ group.master.name }}, networks, { get_attr: [north_private_net, name] }, 0 ] } - {{ group.master.name }}_instance_name: - value: { get_attr: [ {{ group.master.name }}, instance_name ] } - {{ group.slave.name }}_ip: - value: { get_attr: [ {{ group.slave.name }}_floating_ip, floating_ip_address ] } - {{ group.slave.name }}_instance_name: - value: { get_attr: [ {{ group.slave.name }}, instance_name ] } +{% for agent in agents.value() %} + {{ agent.id }}_instance_name: + value: { get_attr: [ {{ agent.id }}, instance_name ] } +{% if agent.mode == 'master' %} + {{ agent.id }}_ip: + value: { get_attr: [ {{ agent.id }}, networks, { get_attr: [east_private_net, name] }, 0 ] } +{% else %} + {{ agent.id }}_ip: + value: { get_attr: [ {{ agent.id }}_floating_ip, floating_ip_address ] } +{% endif %} {% endfor %} diff --git a/scenarios/storage/dummy.hot b/scenarios/storage/dummy.hot index 5fb3ca6..10897e4 100644 --- a/scenarios/storage/dummy.hot +++ b/scenarios/storage/dummy.hot @@ -43,17 +43,17 @@ resources: router_id: { get_resource: router } subnet_id: { get_resource: private_subnet } -{% for group in groups %} +{% for agent in agents.values() %} - {{ group.master.name }}: + {{ agent.id }}: type: OS::Nova::Server properties: - name: {{ group.master.name }} + name: {{ agent.id }} image: { get_param: image } flavor: { get_param: flavor } - availability_zone: "nova:{{ group.master.node }}" + availability_zone: "nova:{{ agent.node }}" networks: - - port: { get_resource: {{ group.master.name }}_port } + - port: { get_resource: {{ agent.id }}_port } user_data_format: RAW user_data: str_replace: @@ -62,9 +62,9 @@ resources: screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID params: "$SERVER_ENDPOINT": { get_param: server_endpoint } - "$AGENT_ID": {{ group.slave.name }} + "$AGENT_ID": {{ agent.id }} - {{ group.master.name }}_port: + {{ agent.id }}_port: type: OS::Neutron::Port properties: network_id: { get_resource: private_net } @@ -74,9 +74,9 @@ resources: {% endfor %} outputs: -{% for group in groups %} - {{ group.master.name }}_ip: - value: { get_attr: [ {{ group.master.name }}, networks, { get_attr: [private_net, name] }, 0 ] } - {{ group.master.name }}_instance_name: - value: { get_attr: [ {{ group.master.name }}, instance_name ] } +{% for agent in agents.values() %} + {{ agent.id }}_instance_name: + value: { get_attr: [ {{ agent.id }}, instance_name ] } + {{ agent.id }}_ip: + value: { get_attr: [ {{ agent.id }}, networks, { get_attr: [private_net, name] }, 0 ] } {% endfor %} diff --git a/shaker/engine/deploy.py b/shaker/engine/deploy.py index 4234c69..ec75093 100644 --- a/shaker/engine/deploy.py +++ b/shaker/engine/deploy.py @@ -26,6 +26,85 @@ from shaker.openstack.clients import openstack LOG = logging.getLogger(__name__) +def generate_agents(compute_nodes, vm_accommodation, unique): + cn_count = len(compute_nodes) + iterations = cn_count + if 'single_room' in vm_accommodation and 'pair' in vm_accommodation: + iterations //= 2 + node_formulae = lambda x: compute_nodes[x % cn_count] + + agents = {} + + for i in range(iterations): + if 'pair' in vm_accommodation: + master_id = '%s_master_%s' % (unique, i) + slave_id = '%s_slave_%s' % (unique, i) + master = dict(id=master_id, mode='master', slave_id=slave_id) + slave = dict(id=slave_id, mode='slave', master_id=master_id) + + if 'single_room' in vm_accommodation: + master['node'] = node_formulae(i * 2) + slave['node'] = node_formulae(i * 2 + 1) + elif 'double_room' in vm_accommodation: + master['node'] = node_formulae(i) + slave['node'] = node_formulae(i) + elif 'mixed_room' in vm_accommodation: + master['node'] = node_formulae(i) + slave['node'] = node_formulae(i + 1) + + agents[master['id']] = master + agents[slave['id']] = slave + else: + if 'single_room' in vm_accommodation: + agent_id = '%s_agent_%s' % (unique, i) + agents[agent_id] = dict(id=agent_id, node=node_formulae(i), + mode='alone') + + return agents + + +def _get_stack_values(stack_outputs, vm_name, params): + result = {} + for param in params: + o = stack_outputs.get(vm_name + '_' + param) + if o: + result[param] = o + return result + + +def filter_agents(agents, stack_outputs): + deployed_agents = {} + + # first pass, ignore non-deployed + for agent in agents.values(): + stack_values = _get_stack_values(stack_outputs, agent['id'], + ['ip', 'instance_name']) + if not stack_values.get('instance_name'): + LOG.info('Ignore non-deployed agent: %s', agent) + continue + + agent.update(stack_values) + + # workaround of Nova bug 1422686 + if agent.get('mode') == 'slave' and not agent.get('ip'): + LOG.info('IP address is missing in agent: %s', agent) + continue + + deployed_agents[agent['id']] = agent + + # second pass, check pairs + result = {} + for agent in deployed_agents.values(): + if (agent.get('mode') == 'alone' or + (agent.get('mode') == 'master' and + agent.get('slave_id') in deployed_agents) or + (agent.get('mode') == 'slave' and + agent.get('master_id') in deployed_agents)): + result[agent['id']] = agent + + return result + + class Deployment(object): def __init__(self, os_username, os_password, os_tenant_name, os_auth_url, os_region_name, server_endpoint, external_net, flavor_name, @@ -45,87 +124,15 @@ class Deployment(object): self.stack_name = 'shaker_%s' % utils.random_string() self.stack_deployed = False - def _make_groups(self, vm_accommodation): - compute_nodes = nova.get_available_compute_nodes( - self.openstack_client.nova) - cn_count = len(compute_nodes) - iterations = cn_count - if 'single_room' in vm_accommodation and 'pair' in vm_accommodation: - iterations /= 2 - node_formulae = lambda x: compute_nodes[x % cn_count] - - groups = [] - - for i in range(iterations): - group = dict( - master=dict(name=('%s_master_%s' % (self.stack_name, i))), - slave=dict(name=('%s_slave_%s' % (self.stack_name, i)))) - - if 'pair' in vm_accommodation: - if 'single_room' in vm_accommodation: - group['master']['node'] = node_formulae(i * 2) - group['slave']['node'] = node_formulae(i * 2 + 1) - elif 'double_room' in vm_accommodation: - group['master']['node'] = node_formulae(i) - group['slave']['node'] = node_formulae(i) - elif 'mixed_room' in vm_accommodation: - group['master']['node'] = node_formulae(i) - group['slave']['node'] = node_formulae(i + 1) - else: - if 'single_room' in vm_accommodation: - group['master']['node'] = node_formulae(i) - groups.append(group) - - return groups - - def _get_outputs(self, stack_outputs, vm_name, params): - result = {} - for param in params: - o = stack_outputs.get(vm_name + '_' + param) - if o: - result[param] = o - return result - - def _make_agents(self, groups, outputs): - agents = [] - - for group in groups: - master = self._get_outputs(outputs, group['master']['name'], - ['ip', 'instance_name']) - if not master.get('instance_name'): - LOG.info('Group is not deployed: %s. Ignoring', group) - continue - - master.update(group['master']) - master.update(dict(mode='master', id=group['master']['name'])) - - slave = self._get_outputs(outputs, group['slave']['name'], - ['ip', 'instance_name']) - - # workaround of Nova bug 1422686 - if slave.get('instance_name') and not slave.get('ip'): - LOG.info('Ignoring group because of missing IP: %s', group) - continue - - agents.append(master) - - if slave.get('instance_name'): - # slave is deployed - slave.update(group['slave']) - slave.update(dict(mode='slave', id=group['slave']['name'])) - - master['slave_id'] = slave['id'] - slave['master_id'] = master['id'] - agents.append(slave) - - return agents - def _deploy_from_hot(self, specification): - groups = self._make_groups(specification['vm_accommodation']) + agents = generate_agents( + nova.get_available_compute_nodes(self.openstack_client.nova), + specification['vm_accommodation'], + self.stack_name) # render template by jinja vars_values = { - 'groups': groups, + 'agents': agents, 'unique': self.stack_name, } heat_template = utils.read_file(specification['template']) @@ -160,19 +167,18 @@ class Deployment(object): outputs = heat.get_stack_outputs(self.openstack_client.heat, stack['id']) - # convert groups into agents - return self._make_agents(groups, outputs) + return filter_agents(agents, outputs) def deploy(self, deployment): - agents = [] + agents = {} if deployment.get('template'): # deploy topology specified by HOT - agents += self._deploy_from_hot(deployment) + agents.update(self._deploy_from_hot(deployment)) if deployment.get('agents'): # agents are specified statically - agents += deployment.get('agents') + agents.update(dict((a['id'], a) for a in deployment.get('agents'))) return agents diff --git a/shaker/engine/server.py b/shaker/engine/server.py index 819e7e3..65ebe5b 100644 --- a/shaker/engine/server.py +++ b/shaker/engine/server.py @@ -129,29 +129,28 @@ def read_scenario(): return scenario -def _resolve_agent_ids(agents): - id_to_agent = dict((ag['id'], ag) for ag in agents) - for agent in agents: +def _extend_agents(agents): + for agent in agents.values(): if agent.get('slave_id'): - agent['slave'] = id_to_agent[agent['slave_id']] + agent['slave'] = utils.copy_dict_kv(agents[agent['slave_id']]) if agent.get('master_id'): - agent['master'] = id_to_agent[agent['master_id']] + agent['master'] = utils.copy_dict_kv(agents[agent['master_id']]) def _pick_agents(agents, size): - # tests are executed on master agents only - agents = [a for a in agents if a['mode'] == 'master'] + # slave agents do not execute any tests + agents = [a for a in agents.values() if a.get('mode') != 'slave'] if not size or size == 'full': yield agents elif size == 'linear_progression': for i in range(len(agents)): - yield agents[:i] + yield agents[:i + 1] elif size == 'quadratic_progression': n = len(agents) seq = [n] while n > 1: - n /= 2 + n //= 2 seq.append(n) seq.reverse() for i in seq: @@ -159,12 +158,12 @@ def _pick_agents(agents, size): def execute(execution, agents): - _resolve_agent_ids(agents) + _extend_agents(agents) message_queue = MessageQueue(cfg.CONF.server_endpoint) quorum = Quorum(message_queue) - quorum.wait_join(set(a['id'] for a in agents)) + quorum.wait_join(set(agents.keys())) result = [] @@ -216,34 +215,37 @@ def main(): LOG.info('Logging enabled') conf.log_opt_values(LOG, std_logging.DEBUG) - scenario = read_scenario() - deployment = deploy.Deployment(cfg.CONF.os_username, - cfg.CONF.os_password, - cfg.CONF.os_tenant_name, - cfg.CONF.os_auth_url, - cfg.CONF.os_region_name, - cfg.CONF.server_endpoint, - cfg.CONF.external_net, - cfg.CONF.flavor_name, - cfg.CONF.image_name) - agents = deployment.deploy(scenario['deployment']) + deployment = None + try: + scenario = read_scenario() + deployment = deploy.Deployment(cfg.CONF.os_username, + cfg.CONF.os_password, + cfg.CONF.os_tenant_name, + cfg.CONF.os_auth_url, + cfg.CONF.os_region_name, + cfg.CONF.server_endpoint, + cfg.CONF.external_net, + cfg.CONF.flavor_name, + cfg.CONF.image_name) + agents = deployment.deploy(scenario['deployment']) - if not agents: - LOG.info('No agents deployed. Terminating.') - return + if not agents: + LOG.info('No agents deployed. Terminating.') + return - LOG.debug('Agents: %s', agents) + LOG.debug('Agents: %s', agents) - result = execute(scenario['execution'], agents) - LOG.debug('Result: %s', result) + result = execute(scenario['execution'], agents) + LOG.debug('Result: %s', result) - report.generate_report(cfg.CONF.report_template, - cfg.CONF.report, - dict(scenario=yaml.dump(scenario), - agents=agents, - result=result)) - - deployment.cleanup() + report.generate_report(cfg.CONF.report_template, + cfg.CONF.report, + dict(scenario=yaml.dump(scenario), + agents=agents.values(), + result=result)) + finally: + if deployment: + deployment.cleanup() if __name__ == "__main__": diff --git a/shaker/engine/utils.py b/shaker/engine/utils.py index 28d6931..fcbc379 100644 --- a/shaker/engine/utils.py +++ b/shaker/engine/utils.py @@ -71,3 +71,7 @@ def read_uri(uri): def random_string(length=6): return ''.join(random.sample('adefikmoprstuz', length)) + + +def copy_dict_kv(source): + return dict((k, v) for k, v in source.items()) diff --git a/test-requirements.txt b/test-requirements.txt index 579e2ec..702c414 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,6 +3,7 @@ # process, which may cause wedges in the gate later. # Hacking already pins down pep8, pyflakes and flake8 +coverage>=3.6 hacking>=0.8.0,<0.9 mock>=1.0 python-subunit>=0.0.18 diff --git a/tests/test_deploy.py b/tests/test_deploy.py new file mode 100644 index 0000000..7bc3f67 --- /dev/null +++ b/tests/test_deploy.py @@ -0,0 +1,227 @@ +# Copyright (c) 2015 Mirantis Inc. +# +# 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 testtools + +from shaker.engine import deploy + + +class TestDeploy(testtools.TestCase): + + def test_generate_agents_alone_single_room(self): + unique = 'UU1D' + expected = { + 'UU1D_agent_0': { + 'id': 'UU1D_agent_0', + 'mode': 'alone', + 'node': 'uno'}, + 'UU1D_agent_1': { + 'id': 'UU1D_agent_1', + 'mode': 'alone', + 'node': 'dos'}, + } + actual = deploy.generate_agents(['uno', 'dos'], + ['single_room'], + unique) + self.assertEqual(expected, actual) + + def test_generate_agents_pair_single_room(self): + unique = 'UU1D' + expected = { + 'UU1D_master_0': { + 'id': 'UU1D_master_0', + 'mode': 'master', + 'node': 'uno', + 'slave_id': 'UU1D_slave_0'}, + 'UU1D_slave_0': { + 'id': 'UU1D_slave_0', + 'master_id': 'UU1D_master_0', + 'mode': 'slave', + 'node': 'dos'}, + } + actual = deploy.generate_agents(['uno', 'dos', 'tre'], + ['pair', 'single_room'], + unique) + self.assertEqual(expected, actual) + + def test_generate_agents_pair_double_room(self): + unique = 'UU1D' + expected = { + 'UU1D_master_0': { + 'id': 'UU1D_master_0', + 'mode': 'master', + 'node': 'uno', + 'slave_id': 'UU1D_slave_0'}, + 'UU1D_slave_0': { + 'id': 'UU1D_slave_0', + 'master_id': 'UU1D_master_0', + 'mode': 'slave', + 'node': 'uno'}, + 'UU1D_master_1': { + 'id': 'UU1D_master_1', + 'mode': 'master', + 'node': 'dos', + 'slave_id': 'UU1D_slave_1'}, + 'UU1D_slave_1': { + 'id': 'UU1D_slave_1', + 'master_id': 'UU1D_master_1', + 'mode': 'slave', + 'node': 'dos'}, + 'UU1D_master_2': { + 'id': 'UU1D_master_2', + 'mode': 'master', + 'node': 'tre', + 'slave_id': 'UU1D_slave_2'}, + 'UU1D_slave_2': { + 'id': 'UU1D_slave_2', + 'master_id': 'UU1D_master_2', + 'mode': 'slave', + 'node': 'tre'}, + } + actual = deploy.generate_agents(['uno', 'dos', 'tre'], + ['pair', 'double_room'], + unique) + self.assertEqual(expected, actual) + + def test_generate_agents_pair_mixed_room(self): + unique = 'UU1D' + expected = { + 'UU1D_master_0': { + 'id': 'UU1D_master_0', + 'mode': 'master', + 'node': 'uno', + 'slave_id': 'UU1D_slave_0'}, + 'UU1D_slave_0': { + 'id': 'UU1D_slave_0', + 'master_id': 'UU1D_master_0', + 'mode': 'slave', + 'node': 'dos'}, + 'UU1D_master_1': { + 'id': 'UU1D_master_1', + 'mode': 'master', + 'node': 'dos', + 'slave_id': 'UU1D_slave_1'}, + 'UU1D_slave_1': { + 'id': 'UU1D_slave_1', + 'master_id': 'UU1D_master_1', + 'mode': 'slave', + 'node': 'uno'}, + } + actual = deploy.generate_agents(['uno', 'dos'], + ['pair', 'mixed_room'], + unique) + self.assertEqual(expected, actual) + + def test_filter_agents_all_deployed(self): + agents = { + 'UU1D_agent_0': { + 'id': 'UU1D_agent_0', + 'mode': 'alone', + 'node': 'uno'}, + 'UU1D_agent_1': { + 'id': 'UU1D_agent_1', + 'mode': 'alone', + 'node': 'dos'}, + } + stack_outputs = { + 'UU1D_agent_0_ip': '10.0.0.1', + 'UU1D_agent_0_instance_name': 'i-000001', + 'UU1D_agent_1_ip': '10.0.0.2', + 'UU1D_agent_1_instance_name': 'i-000002', + } + filtered = deploy.filter_agents(agents, stack_outputs) + self.assertEqual(agents, filtered) + + def test_filter_agents_partial_deployed(self): + agents = { + 'UU1D_agent_0': { + 'id': 'UU1D_agent_0', + 'mode': 'alone', + 'node': 'uno'}, + 'UU1D_agent_1': { + 'id': 'UU1D_agent_1', + 'mode': 'alone', + 'node': 'dos'}, + } + stack_outputs = { + 'UU1D_agent_0_ip': '10.0.0.1', + 'UU1D_agent_0_instance_name': 'i-000001', + } + expected = {'UU1D_agent_0': agents['UU1D_agent_0']} + filtered = deploy.filter_agents(agents, stack_outputs) + self.assertEqual(expected, filtered) + + def test_filter_agents_pair_single_room(self): + agents = { + 'UU1D_master_0': { + 'id': 'UU1D_master_0', + 'mode': 'master', + 'node': 'uno', + 'slave_id': 'UU1D_slave_0'}, + 'UU1D_slave_0': { + 'id': 'UU1D_slave_0', + 'master_id': 'UU1D_master_0', + 'mode': 'slave', + 'node': 'dos'}, + } + stack_outputs = { + 'UU1D_master_0_ip': '10.0.0.1', + 'UU1D_master_0_instance_name': 'i-000001', + 'UU1D_slave_0_ip': '10.0.0.2', + 'UU1D_slave_0_instance_name': 'i-000002', + } + expected = {'UU1D_master_0': agents['UU1D_master_0'], + 'UU1D_slave_0': agents['UU1D_slave_0']} + + filtered = deploy.filter_agents(agents, stack_outputs) + self.assertEqual(expected, filtered) + + def test_filter_agents_pair_double_room_partially_deployed(self): + agents = { + 'UU1D_master_0': { + 'id': 'UU1D_master_0', + 'mode': 'master', + 'node': 'uno', + 'slave_id': 'UU1D_slave_0'}, + 'UU1D_slave_0': { + 'id': 'UU1D_slave_0', + 'master_id': 'UU1D_master_0', + 'mode': 'slave', + 'node': 'uno'}, + 'UU1D_master_1': { + 'id': 'UU1D_master_1', + 'mode': 'master', + 'node': 'dos', + 'slave_id': 'UU1D_slave_1'}, + 'UU1D_slave_1': { + 'id': 'UU1D_slave_1', + 'master_id': 'UU1D_master_1', + 'mode': 'slave', + 'node': 'dos'}, + } + stack_outputs = { + 'UU1D_master_0_ip': '10.0.0.1', + 'UU1D_master_0_instance_name': 'i-000001', + 'UU1D_slave_0_ip': '10.0.0.2', + 'UU1D_slave_0_instance_name': 'i-000002', + 'UU1D_master_1_ip': '10.0.0.3', + 'UU1D_master_1_instance_name': 'i-000003', + 'UU1D_slave_1_instance_name': 'i-000004', + } + expected = {'UU1D_master_0': agents['UU1D_master_0'], + 'UU1D_slave_0': agents['UU1D_slave_0'], } + + filtered = deploy.filter_agents(agents, stack_outputs) + self.assertEqual(expected, filtered) diff --git a/tests/test_server.py b/tests/test_server.py new file mode 100644 index 0000000..bd28467 --- /dev/null +++ b/tests/test_server.py @@ -0,0 +1,88 @@ +# Copyright (c) 2015 Mirantis Inc. +# +# 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 testtools + +from shaker.engine import server + + +class TestServer(testtools.TestCase): + + def test_extend_agents(self): + agents = { + 'UU1D_master_0': { + 'id': 'UU1D_master_0', + 'mode': 'master', + 'node': 'uno', + 'slave_id': 'UU1D_slave_0'}, + 'UU1D_slave_0': { + 'id': 'UU1D_slave_0', + 'master_id': 'UU1D_master_0', + 'mode': 'slave', + 'node': 'dos'}, + } + server._extend_agents(agents) + self.assertDictContainsSubset(agents['UU1D_master_0']['slave'], + agents['UU1D_slave_0']) + self.assertDictContainsSubset(agents['UU1D_slave_0']['master'], + agents['UU1D_master_0']) + + def test_pick_agents_full(self): + agents = {} + for i in range(10): + agents[i] = { + 'id': i, 'mode': 'alone', 'node': 'uno', + } + picked = [set(a['id'] for a in arr) + for arr in server._pick_agents(agents, 'full')] + self.assertEqual([set(range(10))], picked) + + def test_pick_agents_full_filter_slaves(self): + agents = {} + for i in range(10): + agents['master_%s' % i] = { + 'id': 'master_%s' % i, 'mode': 'master', 'node': 'uno', + } + agents['slave_%s' % i] = { + 'id': 'slave_%s' % i, 'mode': 'slave', 'node': 'uno', + } + picked = [set(a['id'] for a in arr) + for arr in server._pick_agents(agents, 'full')] + self.assertEqual([set('master_%s' % i for i in range(10))], + picked) + + def test_pick_agents_linear(self): + agents = {} + for i in range(10): + agents[i] = { + 'id': i, 'mode': 'alone', 'node': 'uno', + } + picked = [set(a['id'] for a in arr) + for arr in server._pick_agents(agents, 'linear_progression')] + self.assertEqual([set(range(i + 1)) for i in range(0, 10)], + picked) + + def test_pick_agents_quadratic(self): + agents = {} + for i in range(10): + agents[i] = { + 'id': i, 'mode': 'alone', 'node': 'uno', + } + picked = [set(a['id'] for a in arr) + for arr in server._pick_agents(agents, + 'quadratic_progression')] + self.assertEqual([set(range(1)), set(range(2)), + set(range(5)), set(range(10))], + picked)