summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/ansible/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/ansible/tests')
-rw-r--r--src/pybind/mgr/ansible/tests/__init__.py0
-rw-r--r--src/pybind/mgr/ansible/tests/pb_execution_events.data183
-rw-r--r--src/pybind/mgr/ansible/tests/test_client_playbooks.py287
-rw-r--r--src/pybind/mgr/ansible/tests/test_output_wizards.py207
4 files changed, 677 insertions, 0 deletions
diff --git a/src/pybind/mgr/ansible/tests/__init__.py b/src/pybind/mgr/ansible/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/pybind/mgr/ansible/tests/__init__.py
diff --git a/src/pybind/mgr/ansible/tests/pb_execution_events.data b/src/pybind/mgr/ansible/tests/pb_execution_events.data
new file mode 100644
index 00000000..248134a3
--- /dev/null
+++ b/src/pybind/mgr/ansible/tests/pb_execution_events.data
@@ -0,0 +1,183 @@
+{
+ "status": "OK",
+ "msg": "",
+ "data": {
+ "events": {
+ "2-6edf768f-2923-44e1-b884-f0227b811cfc": {
+ "event": "playbook_on_start"
+ },
+ "3-2016b900-e38f-7dcd-a2e7-000000000008": {
+ "event": "playbook_on_play_start"
+ },
+ "4-2016b900-e38f-7dcd-a2e7-000000000012": {
+ "event": "playbook_on_task_start",
+ "task": "Gathering Facts"
+ },
+ "5-19ae1e5e-aa2d-479e-845a-ef0253cc1f99": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.245",
+ "task": "Gathering Facts"
+ },
+ "6-aad3acc4-06a3-4c97-82ff-31e9e484b1f5": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.61",
+ "task": "Gathering Facts"
+ },
+ "7-55298017-3e7d-4734-b316-bbe13ce1da5e": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.254",
+ "task": "Gathering Facts"
+ },
+ "8-2016b900-e38f-7dcd-a2e7-00000000000a": {
+ "event": "playbook_on_task_start",
+ "task": "setup"
+ },
+ "9-2085ccb6-e337-4b9f-bc38-1d8bbf9b973f": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.254",
+ "task": "setup"
+ },
+ "10-e14cdbbc-4883-436c-a41c-a8194ec69075": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.245",
+ "task": "setup"
+ },
+ "11-6d815a26-df53-4240-b8b6-2484e88e4f48": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.61",
+ "task": "setup"
+ },
+ "12-2016b900-e38f-7dcd-a2e7-00000000000b": {
+ "event": "playbook_on_task_start",
+ "task": "Get a list of block devices (excludes loop and child devices)"
+ },
+ "13-799b0119-ccab-4eca-b30b-a37b0bafa02c": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.245",
+ "task": "Get a list of block devices (excludes loop and child devices)"
+ },
+ "14-6beb6958-4bfd-4a9c-bd2c-d20d00248605": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.61",
+ "task": "Get a list of block devices (excludes loop and child devices)"
+ },
+ "15-3ca99cc8-98ea-4967-8f2d-115426d00b6a": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.254",
+ "task": "Get a list of block devices (excludes loop and child devices)"
+ },
+ "16-2016b900-e38f-7dcd-a2e7-00000000000c": {
+ "event": "playbook_on_task_start",
+ "task": "check if disk {{ item }} is free"
+ },
+ "17-8c88141a-08d1-411f-a855-9f7702a49c4e": {
+ "event": "runner_item_on_failed",
+ "host": "192.168.121.245",
+ "task": "check if disk vda is free"
+ },
+ "18-4457db98-6f18-4f63-bfaa-584db5eea05b": {
+ "event": "runner_on_failed",
+ "host": "192.168.121.245",
+ "task": "check if disk {{ item }} is free"
+ },
+ "19-ac3c72cd-1fbb-495a-be69-53fa6029f356": {
+ "event": "runner_item_on_failed",
+ "host": "192.168.121.61",
+ "task": "check if disk vda is free"
+ },
+ "20-d161cb70-ba2e-4571-b029-c6428a566fef": {
+ "event": "runner_on_failed",
+ "host": "192.168.121.61",
+ "task": "check if disk {{ item }} is free"
+ },
+ "21-65f1ce5c-2d86-4cc3-8e10-cff6bf6cbd82": {
+ "event": "runner_item_on_failed",
+ "host": "192.168.121.254",
+ "task": "check if disk sda is free"
+ },
+ "22-7f86dcd4-4ef7-4f5a-9db3-c3780b67cc4b": {
+ "event": "runner_item_on_failed",
+ "host": "192.168.121.254",
+ "task": "check if disk sdb is free"
+ },
+ "23-837bf4f6-a912-46a8-b94b-55aa66a935c4": {
+ "event": "runner_item_on_ok",
+ "host": "192.168.121.254",
+ "task": "check if disk sdc is free"
+ },
+ "24-adf6238d-723f-4783-9226-8475419d466e": {
+ "event": "runner_item_on_failed",
+ "host": "192.168.121.254",
+ "task": "check if disk vda is free"
+ },
+ "25-554661d8-bc34-4885-a589-4960d6b8a487": {
+ "event": "runner_on_failed",
+ "host": "192.168.121.254",
+ "task": "check if disk {{ item }} is free"
+ },
+ "26-2016b900-e38f-7dcd-a2e7-00000000000d": {
+ "event": "playbook_on_task_start",
+ "task": "Update hosts freedisk list"
+ },
+ "27-52df484c-30a0-4e3b-9057-02ca345c5790": {
+ "event": "runner_item_on_skipped",
+ "host": "192.168.121.254",
+ "task": "Update hosts freedisk list"
+ },
+ "28-083616ad-3c1f-4fb8-a06c-5d64e670e362": {
+ "event": "runner_item_on_skipped",
+ "host": "192.168.121.254",
+ "task": "Update hosts freedisk list"
+ },
+ "29-bffc68d3-5448-491f-8780-07858285f5cd": {
+ "event": "runner_item_on_skipped",
+ "host": "192.168.121.245",
+ "task": "Update hosts freedisk list"
+ },
+ "30-cca2dfd9-16e9-4fcb-8bf7-c4da7dab5668": {
+ "event": "runner_on_skipped",
+ "host": "192.168.121.245",
+ "task": "Update hosts freedisk list"
+ },
+ "31-158a98ac-7e8d-4ebb-8c53-4467351a2d3a": {
+ "event": "runner_item_on_ok",
+ "host": "192.168.121.254",
+ "task": "Update hosts freedisk list"
+ },
+ "32-06a7e809-8d82-41df-b01d-45d94e519cb7": {
+ "event": "runner_item_on_skipped",
+ "host": "192.168.121.254",
+ "task": "Update hosts freedisk list"
+ },
+ "33-d5cdbb58-728a-4be5-abf1-4a051146e727": {
+ "event": "runner_item_on_skipped",
+ "host": "192.168.121.61",
+ "task": "Update hosts freedisk list"
+ },
+ "34-9b3c570b-22d8-4539-8c94-d0c1cbed8633": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.254",
+ "task": "Update hosts freedisk list"
+ },
+ "35-93336830-03cd-43ff-be87-a7e063ca7547": {
+ "event": "runner_on_skipped",
+ "host": "192.168.121.61",
+ "task": "Update hosts freedisk list"
+ },
+ "36-2016b900-e38f-7dcd-a2e7-00000000000e": {
+ "event": "playbook_on_task_start",
+ "task": "RESULTS"
+ },
+ "37-100564f1-9fed-48c2-bd62-4ae8636dfcdb": {
+ "event": "runner_on_ok",
+ "host": "192.168.121.254",
+ "task": "RESULTS"
+ },
+ "38-20a64160-30a1-481f-a3ee-36e491bc7869": {
+ "event": "playbook_on_stats"
+ }
+ },
+ "total_events": 37
+ }
+}
+
diff --git a/src/pybind/mgr/ansible/tests/test_client_playbooks.py b/src/pybind/mgr/ansible/tests/test_client_playbooks.py
new file mode 100644
index 00000000..98dfd3dd
--- /dev/null
+++ b/src/pybind/mgr/ansible/tests/test_client_playbooks.py
@@ -0,0 +1,287 @@
+import logging
+import unittest
+import mock
+import json
+
+import requests_mock
+
+from requests.exceptions import ConnectionError
+
+from ..ansible_runner_svc import Client, PlayBookExecution, ExecutionStatusCode, \
+ LOGIN_URL, API_URL, PLAYBOOK_EXEC_URL, \
+ PLAYBOOK_EVENTS, AnsibleRunnerServiceError
+
+
+SERVER_URL = "ars:5001"
+USER = "admin"
+PASSWORD = "admin"
+CERTIFICATE = ""
+
+# Playbook attributes
+PB_NAME = "test_playbook"
+PB_UUID = "1733c3ac"
+
+# Playbook execution data file
+PB_EVENTS_FILE = "./tests/pb_execution_events.data"
+
+# create console handler and set level to info
+logger = logging.getLogger()
+handler = logging.StreamHandler()
+handler.setLevel(logging.INFO)
+formatter = logging.Formatter("%(levelname)s - %(message)s")
+handler.setFormatter(formatter)
+logger.addHandler(handler)
+
+
+def mock_login(mock_server):
+
+ the_login_url = "https://%s/%s" % (SERVER_URL,LOGIN_URL)
+
+ mock_server.register_uri("GET",
+ the_login_url,
+ json={"status": "OK",
+ "msg": "Token returned",
+ "data": {"token": "dummy_token"}},
+ status_code=200)
+
+ the_api_url = "https://%s/%s" % (SERVER_URL,API_URL)
+ mock_server.register_uri("GET",
+ the_api_url,
+ text="<!DOCTYPE html>api</html>",
+ status_code=200)
+
+def mock_get_pb(mock_server, playbook_name, return_code):
+
+ mock_login(mock_server)
+
+ ars_client = Client(SERVER_URL, USER, PASSWORD,
+ CERTIFICATE, logger)
+
+ the_pb_url = "https://%s/%s/%s" % (SERVER_URL, PLAYBOOK_EXEC_URL, playbook_name)
+
+ if return_code == 404:
+ mock_server.register_uri("POST",
+ the_pb_url,
+ json={ "status": "NOTFOUND",
+ "msg": "playbook file not found",
+ "data": {}},
+ status_code=return_code)
+ elif return_code == 202:
+ mock_server.register_uri("POST",
+ the_pb_url,
+ json={ "status": "STARTED",
+ "msg": "starting",
+ "data": { "play_uuid": "1733c3ac" }},
+ status_code=return_code)
+
+ return PlayBookExecution(ars_client, playbook_name, logger,
+ result_pattern = "RESULTS")
+
+class ARSclientTest(unittest.TestCase):
+
+ def test_server_not_reachable(self):
+
+ with self.assertRaises(AnsibleRunnerServiceError):
+ ars_client = Client(SERVER_URL, USER, PASSWORD,
+ CERTIFICATE, logger)
+
+ def test_server_wrong_USER(self):
+
+ with requests_mock.Mocker() as mock_server:
+ the_login_url = "https://%s/%s" % (SERVER_URL,LOGIN_URL)
+ mock_server.get(the_login_url,
+ json={"status": "NOAUTH",
+ "msg": "Access denied invalid login: unknown USER",
+ "data": {}},
+ status_code=401)
+
+
+ ars_client = Client(SERVER_URL, USER, PASSWORD,
+ CERTIFICATE, logger)
+
+ self.assertFalse(ars_client.is_operative(),
+ "Operative attribute expected to be False")
+
+ def test_server_connection_ok(self):
+
+ with requests_mock.Mocker() as mock_server:
+
+ mock_login(mock_server)
+
+ ars_client = Client(SERVER_URL, USER, PASSWORD,
+ CERTIFICATE, logger)
+
+ self.assertTrue(ars_client.is_operative(),
+ "Operative attribute expected to be True")
+
+ def test_server_http_delete(self):
+
+ with requests_mock.Mocker() as mock_server:
+
+ mock_login(mock_server)
+
+ ars_client = Client(SERVER_URL, USER, PASSWORD,
+ CERTIFICATE, logger)
+
+ url = "https://%s/test" % (SERVER_URL)
+ mock_server.register_uri("DELETE",
+ url,
+ json={ "status": "OK",
+ "msg": "",
+ "data": {}},
+ status_code=201)
+
+ response = ars_client.http_delete("test")
+ self.assertTrue(response.status_code == 201)
+
+class PlayBookExecutionTests(unittest.TestCase):
+
+
+ def test_playbook_execution_ok(self):
+ """Check playbook id is set when the playbook is launched
+ """
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, PB_NAME, 202)
+
+ test_pb.launch()
+
+ self.assertEqual(test_pb.play_uuid, PB_UUID,
+ "Found Unexpected playbook uuid")
+
+
+
+ def test_playbook_execution_error(self):
+ """Check playbook id is not set when the playbook is not present
+ """
+
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, "unknown_playbook", 404)
+
+ with self.assertRaises(AnsibleRunnerServiceError):
+ test_pb.launch()
+
+ #self.assertEqual(test_pb.play_uuid, "",
+ # "Playbook uuid not empty")
+
+ def test_playbook_not_launched(self):
+ """Check right status code when Playbook execution has not been launched
+ """
+
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, PB_NAME, 202)
+
+ # Check playbook not launched
+ self.assertEqual(test_pb.get_status(),
+ ExecutionStatusCode.NOT_LAUNCHED,
+ "Wrong status code for playbook not launched")
+
+ def test_playbook_launched(self):
+ """Check right status code when Playbook execution has been launched
+ """
+
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, PB_NAME, 202)
+
+ test_pb.launch()
+
+ the_status_url = "https://%s/%s/%s" % (SERVER_URL,
+ PLAYBOOK_EXEC_URL,
+ PB_UUID)
+ mock_server.register_uri("GET",
+ the_status_url,
+ json={"status": "OK",
+ "msg": "running",
+ "data": {"task": "Step 2",
+ "last_task_num": 6}
+ },
+ status_code=200)
+
+ self.assertEqual(test_pb.get_status(),
+ ExecutionStatusCode.ON_GOING,
+ "Wrong status code for a running playbook")
+
+ self.assertEqual(test_pb.play_uuid, PB_UUID,
+ "Unexpected playbook uuid")
+
+ def test_playbook_finish_ok(self):
+ """Check right status code when Playbook execution is succesful
+ """
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, PB_NAME, 202)
+
+ test_pb.launch()
+
+ the_status_url = "https://%s/%s/%s" % (SERVER_URL,
+ PLAYBOOK_EXEC_URL,
+ PB_UUID)
+ mock_server.register_uri("GET",
+ the_status_url,
+ json={"status": "OK",
+ "msg": "successful",
+ "data": {}
+ },
+ status_code=200)
+
+ self.assertEqual(test_pb.get_status(),
+ ExecutionStatusCode.SUCCESS,
+ "Wrong status code for a playbook executed succesfully")
+
+ def test_playbook_finish_error(self):
+ """Check right status code when Playbook execution has failed
+ """
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, PB_NAME, 202)
+
+ test_pb.launch()
+
+ the_status_url = "https://%s/%s/%s" % (SERVER_URL,
+ PLAYBOOK_EXEC_URL,
+ PB_UUID)
+ mock_server.register_uri("GET",
+ the_status_url,
+ json={"status": "OK",
+ "msg": "failed",
+ "data": {}
+ },
+ status_code=200)
+
+ self.assertEqual(test_pb.get_status(),
+ ExecutionStatusCode.ERROR,
+ "Wrong status code for a playbook with error")
+
+ def test_playbook_get_result(self):
+ """ Find the right result event in a set of different events
+ """
+ with requests_mock.Mocker() as mock_server:
+
+ test_pb = mock_get_pb(mock_server, PB_NAME, 202)
+
+ test_pb.launch()
+
+ the_events_url = "https://%s/%s" % (SERVER_URL,
+ PLAYBOOK_EVENTS % PB_UUID)
+
+ # Get the events stored in a file
+ pb_events = {}
+ with open(PB_EVENTS_FILE) as events_file:
+ pb_events = json.loads(events_file.read())
+
+ mock_server.register_uri("GET",
+ the_events_url,
+ json=pb_events,
+ status_code=200)
+
+ result = test_pb.get_result("runner_on_ok")
+
+ self.assertEqual(len(result.keys()), 1,
+ "Unique result event not found")
+
+ self.assertIn("37-100564f1-9fed-48c2-bd62-4ae8636dfcdb",
+ result.keys(),
+ "Predefined result event not found")
diff --git a/src/pybind/mgr/ansible/tests/test_output_wizards.py b/src/pybind/mgr/ansible/tests/test_output_wizards.py
new file mode 100644
index 00000000..2a3a9017
--- /dev/null
+++ b/src/pybind/mgr/ansible/tests/test_output_wizards.py
@@ -0,0 +1,207 @@
+""" Test output wizards
+"""
+import unittest
+import mock
+
+from ..ansible_runner_svc import EVENT_DATA_URL
+from ..output_wizards import ProcessHostsList, ProcessPlaybookResult, \
+ ProcessInventory
+
+class OutputWizardProcessHostsList(unittest.TestCase):
+ """Test ProcessHostsList Output Wizard
+ """
+ RESULT_OK = """
+ {
+ "status": "OK",
+ "msg": "",
+ "data": {
+ "hosts": [
+ "host_a",
+ "host_b",
+ "host_c"
+ ]
+ }
+ }
+ """
+ ar_client = mock.Mock()
+ logger = mock.Mock()
+ test_wizard = ProcessHostsList(ar_client, logger)
+
+ def test_process(self):
+ """Test a normal call"""
+
+ nodes_list = self.test_wizard.process("", self.RESULT_OK)
+ self.assertEqual([node.name for node in nodes_list],
+ ["host_a", "host_b", "host_c"])
+
+ def test_errors(self):
+ """Test different kind of errors processing result"""
+
+ # Malformed json
+ host_list = self.test_wizard.process("", """{"msg": """"")
+ self.assertEqual(host_list, [])
+
+ # key error
+ host_list = self.test_wizard.process("", """{"msg": ""}""")
+ self.assertEqual(host_list, [])
+
+ # Hosts not in iterable
+ host_list = self.test_wizard.process("", """{"data":{"hosts": 123} }""")
+ self.assertEqual(host_list, [])
+
+class OutputWizardProcessPlaybookResult(unittest.TestCase):
+ """Test ProcessPlaybookResult Output Wizard
+ """
+ # Input to process
+ INVENTORY_EVENTS = {1:"first event", 2:"second event"}
+ EVENT_INFORMATION = "event information\n"
+
+ # Mocked response
+ mocked_response = mock.Mock()
+ mocked_response.text = EVENT_INFORMATION
+
+ # The Ansible Runner Service client
+ ar_client = mock.Mock()
+ ar_client.http_get = mock.MagicMock(return_value=mocked_response)
+
+ logger = mock.Mock()
+
+ test_wizard = ProcessPlaybookResult(ar_client, logger)
+
+ def test_process(self):
+ """Test a normal call
+ """
+
+ operation_id = 24
+ result = self.test_wizard.process(operation_id, self.INVENTORY_EVENTS)
+
+ # Check http request are correct and compose expected result
+ expected_result = ""
+ for key, dummy_data in self.INVENTORY_EVENTS.items():
+ http_request = EVENT_DATA_URL % (operation_id, key)
+ self.ar_client.http_get.assert_any_call(http_request)
+ expected_result += self.EVENT_INFORMATION
+
+ #Check result
+ self.assertEqual(result, expected_result)
+
+class OutputWizardProcessInventory(unittest.TestCase):
+ """Test ProcessInventory Output Wizard
+ """
+ # Input to process
+ INVENTORY_EVENTS = {'event_uuid_1': {'host': '192.168.121.144',
+ 'task': 'list storage inventory',
+ 'event': 'runner_on_ok'}}
+ EVENT_DATA = r"""
+ {
+ "status": "OK",
+ "msg": "",
+ "data": {
+ "uuid": "5e96d509-174d-4f5f-bd94-e278c3a5b85b",
+ "counter": 11,
+ "stdout": "changed: [192.168.121.144]",
+ "start_line": 17,
+ "end_line": 18,
+ "runner_ident": "6e98b2ba-3ce1-11e9-be81-2016b900e38f",
+ "created": "2019-03-02T11:50:56.582112",
+ "pid": 482,
+ "event_data": {
+ "play_pattern": "osds",
+ "play": "query each host for storage device inventory",
+ "task": "list storage inventory",
+ "task_args": "_ansible_version=2.6.5, _ansible_selinux_special_fs=['fuse', 'nfs', 'vboxsf', 'ramfs', '9p'], _ansible_no_log=False, _ansible_module_name=ceph_volume, _ansible_debug=False, _ansible_verbosity=0, _ansible_keep_remote_files=False, _ansible_syslog_facility=LOG_USER, _ansible_socket=None, action=inventory, _ansible_diff=False, _ansible_remote_tmp=~/.ansible/tmp, _ansible_shell_executable=/bin/sh, _ansible_check_mode=False, _ansible_tmpdir=None",
+ "remote_addr": "192.168.121.144",
+ "res": {
+ "_ansible_parsed": true,
+ "stderr_lines": [],
+ "changed": true,
+ "end": "2019-03-02 11:50:56.554937",
+ "_ansible_no_log": false,
+ "stdout": "[{\"available\": true, \"rejected_reasons\": [], \"sys_api\": {\"scheduler_mode\": \"noop\", \"rotational\": \"1\", \"vendor\": \"ATA\", \"human_readable_size\": \"50.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {}, \"rev\": \"2.5+\", \"sas_address\": \"\", \"locked\": 0, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/sdc\", \"support_discard\": \"\", \"model\": \"QEMU HARDDISK\", \"ro\": \"0\", \"nr_requests\": \"128\", \"size\": 53687091200.0}, \"lvs\": [], \"path\": \"/dev/sdc\"}, {\"available\": false, \"rejected_reasons\": [\"locked\"], \"sys_api\": {\"scheduler_mode\": \"noop\", \"rotational\": \"1\", \"vendor\": \"ATA\", \"human_readable_size\": \"50.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {}, \"rev\": \"2.5+\", \"sas_address\": \"\", \"locked\": 1, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/sda\", \"support_discard\": \"\", \"model\": \"QEMU HARDDISK\", \"ro\": \"0\", \"nr_requests\": \"128\", \"size\": 53687091200.0}, \"lvs\": [{\"cluster_name\": \"ceph\", \"name\": \"osd-data-dcf8a88c-5546-42d2-afa4-b36f7fb23b66\", \"osd_id\": \"3\", \"cluster_fsid\": \"30d61f3e-7ee4-4bdc-8fe7-2ad5bb3f5317\", \"type\": \"block\", \"block_uuid\": \"fVqujC-9dgh-cN9W-1XD4-zVx1-1UdA-fUS3ha\", \"osd_fsid\": \"8b7cbeba-5e86-44ff-a5f3-2e7df77753fe\"}], \"path\": \"/dev/sda\"}, {\"available\": false, \"rejected_reasons\": [\"locked\"], \"sys_api\": {\"scheduler_mode\": \"noop\", \"rotational\": \"1\", \"vendor\": \"ATA\", \"human_readable_size\": \"50.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {}, \"rev\": \"2.5+\", \"sas_address\": \"\", \"locked\": 1, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/sdb\", \"support_discard\": \"\", \"model\": \"QEMU HARDDISK\", \"ro\": \"0\", \"nr_requests\": \"128\", \"size\": 53687091200.0}, \"lvs\": [{\"cluster_name\": \"ceph\", \"name\": \"osd-data-8c92e986-bd97-4b3d-ba77-2cb88e15d80f\", \"osd_id\": \"1\", \"cluster_fsid\": \"30d61f3e-7ee4-4bdc-8fe7-2ad5bb3f5317\", \"type\": \"block\", \"block_uuid\": \"mgzO7O-vUfu-H3mf-4R3K-2f97-ZMRH-SngBFP\", \"osd_fsid\": \"6d067688-3e1b-45f9-ad03-8abd19e9f117\"}], \"path\": \"/dev/sdb\"}, {\"available\": false, \"rejected_reasons\": [\"locked\"], \"sys_api\": {\"scheduler_mode\": \"mq-deadline\", \"rotational\": \"1\", \"vendor\": \"0x1af4\", \"human_readable_size\": \"41.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {\"vda1\": {\"start\": \"2048\", \"holders\": [], \"sectorsize\": 512, \"sectors\": \"2048\", \"size\": \"1024.00 KB\"}, \"vda3\": {\"start\": \"2101248\", \"holders\": [\"dm-0\", \"dm-1\"], \"sectorsize\": 512, \"sectors\": \"81784832\", \"size\": \"39.00 GB\"}, \"vda2\": {\"start\": \"4096\", \"holders\": [], \"sectorsize\": 512, \"sectors\": \"2097152\", \"size\": \"1024.00 MB\"}}, \"rev\": \"\", \"sas_address\": \"\", \"locked\": 1, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/vda\", \"support_discard\": \"\", \"model\": \"\", \"ro\": \"0\", \"nr_requests\": \"256\", \"size\": 44023414784.0}, \"lvs\": [{\"comment\": \"not used by ceph\", \"name\": \"LogVol00\"}, {\"comment\": \"not used by ceph\", \"name\": \"LogVol01\"}], \"path\": \"/dev/vda\"}]",
+ "cmd": [
+ "ceph-volume",
+ "inventory",
+ "--format=json"
+ ],
+ "rc": 0,
+ "start": "2019-03-02 11:50:55.150121",
+ "stderr": "",
+ "delta": "0:00:01.404816",
+ "invocation": {
+ "module_args": {
+ "wal_vg": null,
+ "wal": null,
+ "dmcrypt": false,
+ "block_db_size": "-1",
+ "journal": null,
+ "objectstore": "bluestore",
+ "db": null,
+ "batch_devices": [],
+ "db_vg": null,
+ "journal_vg": null,
+ "cluster": "ceph",
+ "osds_per_device": 1,
+ "containerized": "False",
+ "crush_device_class": null,
+ "report": false,
+ "data_vg": null,
+ "data": null,
+ "action": "inventory",
+ "journal_size": "5120"
+ }
+ },
+ "stdout_lines": [
+ "[{\"available\": true, \"rejected_reasons\": [], \"sys_api\": {\"scheduler_mode\": \"noop\", \"rotational\": \"1\", \"vendor\": \"ATA\", \"human_readable_size\": \"50.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {}, \"rev\": \"2.5+\", \"sas_address\": \"\", \"locked\": 0, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/sdc\", \"support_discard\": \"\", \"model\": \"QEMU HARDDISK\", \"ro\": \"0\", \"nr_requests\": \"128\", \"size\": 53687091200.0}, \"lvs\": [], \"path\": \"/dev/sdc\"}, {\"available\": false, \"rejected_reasons\": [\"locked\"], \"sys_api\": {\"scheduler_mode\": \"noop\", \"rotational\": \"1\", \"vendor\": \"ATA\", \"human_readable_size\": \"50.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {}, \"rev\": \"2.5+\", \"sas_address\": \"\", \"locked\": 1, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/sda\", \"support_discard\": \"\", \"model\": \"QEMU HARDDISK\", \"ro\": \"0\", \"nr_requests\": \"128\", \"size\": 53687091200.0}, \"lvs\": [{\"cluster_name\": \"ceph\", \"name\": \"osd-data-dcf8a88c-5546-42d2-afa4-b36f7fb23b66\", \"osd_id\": \"3\", \"cluster_fsid\": \"30d61f3e-7ee4-4bdc-8fe7-2ad5bb3f5317\", \"type\": \"block\", \"block_uuid\": \"fVqujC-9dgh-cN9W-1XD4-zVx1-1UdA-fUS3ha\", \"osd_fsid\": \"8b7cbeba-5e86-44ff-a5f3-2e7df77753fe\"}], \"path\": \"/dev/sda\"}, {\"available\": false, \"rejected_reasons\": [\"locked\"], \"sys_api\": {\"scheduler_mode\": \"noop\", \"rotational\": \"1\", \"vendor\": \"ATA\", \"human_readable_size\": \"50.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {}, \"rev\": \"2.5+\", \"sas_address\": \"\", \"locked\": 1, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/sdb\", \"support_discard\": \"\", \"model\": \"QEMU HARDDISK\", \"ro\": \"0\", \"nr_requests\": \"128\", \"size\": 53687091200.0}, \"lvs\": [{\"cluster_name\": \"ceph\", \"name\": \"osd-data-8c92e986-bd97-4b3d-ba77-2cb88e15d80f\", \"osd_id\": \"1\", \"cluster_fsid\": \"30d61f3e-7ee4-4bdc-8fe7-2ad5bb3f5317\", \"type\": \"block\", \"block_uuid\": \"mgzO7O-vUfu-H3mf-4R3K-2f97-ZMRH-SngBFP\", \"osd_fsid\": \"6d067688-3e1b-45f9-ad03-8abd19e9f117\"}], \"path\": \"/dev/sdb\"}, {\"available\": false, \"rejected_reasons\": [\"locked\"], \"sys_api\": {\"scheduler_mode\": \"mq-deadline\", \"rotational\": \"1\", \"vendor\": \"0x1af4\", \"human_readable_size\": \"41.00 GB\", \"sectors\": 0, \"sas_device_handle\": \"\", \"partitions\": {\"vda1\": {\"start\": \"2048\", \"holders\": [], \"sectorsize\": 512, \"sectors\": \"2048\", \"size\": \"1024.00 KB\"}, \"vda3\": {\"start\": \"2101248\", \"holders\": [\"dm-0\", \"dm-1\"], \"sectorsize\": 512, \"sectors\": \"81784832\", \"size\": \"39.00 GB\"}, \"vda2\": {\"start\": \"4096\", \"holders\": [], \"sectorsize\": 512, \"sectors\": \"2097152\", \"size\": \"1024.00 MB\"}}, \"rev\": \"\", \"sas_address\": \"\", \"locked\": 1, \"sectorsize\": \"512\", \"removable\": \"0\", \"path\": \"/dev/vda\", \"support_discard\": \"\", \"model\": \"\", \"ro\": \"0\", \"nr_requests\": \"256\", \"size\": 44023414784.0}, \"lvs\": [{\"comment\": \"not used by ceph\", \"name\": \"LogVol00\"}, {\"comment\": \"not used by ceph\", \"name\": \"LogVol01\"}], \"path\": \"/dev/vda\"}]"
+ ]
+ },
+ "pid": 482,
+ "play_uuid": "2016b900-e38f-0e09-19be-00000000000c",
+ "task_uuid": "2016b900-e38f-0e09-19be-000000000012",
+ "event_loop": null,
+ "playbook_uuid": "e80e66f2-4a78-4a96-aaf6-fbe473f11312",
+ "playbook": "storage-inventory.yml",
+ "task_action": "ceph_volume",
+ "host": "192.168.121.144",
+ "task_path": "/usr/share/ansible-runner-service/project/storage-inventory.yml:29"
+ },
+ "event": "runner_on_ok"
+ }
+ }
+ """
+
+ # Mocked response
+ mocked_response = mock.Mock()
+ mocked_response.text = EVENT_DATA
+
+ # The Ansible Runner Service client
+ ar_client = mock.Mock()
+ ar_client.http_get = mock.MagicMock(return_value=mocked_response)
+
+ logger = mock.Mock()
+
+ test_wizard = ProcessInventory(ar_client, logger)
+
+ def test_process(self):
+ """Test a normal call
+ """
+ operation_id = 12
+ nodes_list = self.test_wizard.process(operation_id, self.INVENTORY_EVENTS)
+
+ for key, dummy_data in self.INVENTORY_EVENTS.items():
+ http_request = EVENT_DATA_URL % (operation_id, key)
+ self.ar_client.http_get.assert_any_call(http_request)
+
+
+ # Only one host
+ self.assertTrue(len(nodes_list), 1)
+
+ # Host retrieved OK
+ self.assertEqual(nodes_list[0].name, "192.168.121.144")
+
+ # Devices
+ self.assertTrue(len(nodes_list[0].devices), 4)
+
+ expected_device_ids = ["/dev/sdc", "/dev/sda", "/dev/sdb", "/dev/vda"]
+ device_ids = [dev.id for dev in nodes_list[0].devices]
+
+ self.assertEqual(expected_device_ids, device_ids)