# Copyright 2015 Rackspace, 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. from ironic_python_agent.tests.functional import base class TestCommands(base.FunctionalBase): """Tests the commands API. These tests are structured monolithically as one test with multiple steps to preserve ordering and ensure IPA state remains consistent across different test runs. """ node = {'uuid': '1', 'properties': {}, 'instance_info': {}} def step_1_get_empty_commands(self): response = self.request('get', 'commands') self.assertEqual({'commands': []}, response) def step_2_run_command(self): # NOTE(mariojv): get_clean_steps always returns the default # HardwareManager clean steps if there's not a more specific HWM. So, # this command succeeds even with an empty node and port. This test's # success is required for steps 3 and 4 to succeed. command = {'name': 'clean.get_clean_steps', 'params': {'node': self.node, 'ports': {}}} response = self.request('post', 'commands', json=command, headers={'Content-Type': 'application/json'}) self.assertIsNone(response['command_error']) def step_3_get_commands(self): # This test relies on step 2 to succeed since step 2 runs the command # we're checking for response = self.request('get', 'commands') self.assertEqual(1, len(response['commands'])) self.assertEqual( 'get_clean_steps', response['commands'][0]['command_name']) def step_4_get_command_by_id(self): # First, we have to query the commands API to retrieve the ID. Make # sure this API call succeeds again, just in case it fails for some # reason after the last test. This test relies on step 2 to succeed # since step 2 runs the command we're checking for. response = self.request('get', 'commands') command_id = response['commands'][0]['id'] command_from_id = self.request( 'get', 'commands/%s' % command_id) self.assertEqual('get_clean_steps', command_from_id['command_name']) def step_5_run_non_existent_command(self): fake_command = {'name': 'bad_extension.fake_command', 'params': {}} self.request('post', 'commands', expect_error=404, json=fake_command) def positive_get_post_command_steps(self): """Returns generator with test steps sorted by step number.""" steps_unsorted = [step for step in dir(self) if step.startswith('step_')] # The lambda retrieves the step number from the function name and casts # it to an integer. This is necessary, otherwise a lexicographic sort # would return ['step_1', 'step_12', 'step_3'] after sorting instead of # ['step_1', 'step_3', 'step_12']. steps = sorted(steps_unsorted, key=lambda s: int(s.split('_', 2)[1])) for name in steps: yield getattr(self, name) def test_positive_get_post_commands(self): for step in self.positive_get_post_command_steps(): step()