Use Ironic API v1.11 to support ENROLL state

This state was introduced in Liberty as a new state for freshly enrolled
nodes, but is only exposed to clients who specify an API version >=
1.11. We add a new --initial-state parameter to `openstack baremetal
import` to enable use of the new state.

Change-Id: If04889c3778827a77236f6d88617211ecc0f4161
This commit is contained in:
Miles Gould
2016-01-25 17:05:22 +00:00
parent 5173959248
commit 20fa1ba84f
4 changed files with 138 additions and 10 deletions

View File

@@ -88,3 +88,5 @@ class TestBaremetal(utils.TestCommand):
self.app.client_manager.baremetal = mock.Mock()
self.app.client_manager.image = mock.Mock()
self.app.client_manager.baremetal_introspection = FakeInspectorClient()
self.app.client_manager._region_name = "Arcadia"
self.app.client_manager.session = mock.Mock()

View File

@@ -326,6 +326,22 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
self.instack_json.close()
self.yaml_file.close()
self.instack_yaml.close()
self.baremetal = self.app.client_manager.baremetal
self.baremetal.http_client.os_ironic_api_version = '1.11'
self.baremetal.node = fakes.FakeBaremetalNodeClient(
states={"ABCDEFGH": "enroll", "IJKLMNOP": "enroll"},
transitions={
("ABCDEFGH", "manage"): "manageable",
("IJKLMNOP", "manage"): "manageable",
("ABCDEFGH", "provide"): "available",
("IJKLMNOP", "provide"): "available",
}
)
self.mock_initial_nodes = {
mock.Mock(uuid="ABCDEFGH", provision_state="enroll"),
mock.Mock(uuid="IJKLMNOP", provision_state="enroll")
}
def tearDown(self):
@@ -347,13 +363,85 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_register_nodes.return_value = self.mock_initial_nodes
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
self.assertEqual(sorted([
('ABCDEFGH', 'manage'), ('IJKLMNOP', 'manage'),
('ABCDEFGH', 'provide'), ('IJKLMNOP', 'provide')
]), sorted(self.baremetal.node.updates))
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
def test_json_import_initial_state_enroll(self, mock_register_nodes):
arglist = [
self.json_file.name,
'--json',
'-s', 'http://localhost',
'--initial-state', 'enroll'
]
verifylist = [
('csv', False),
('json', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_register_nodes.return_value = self.mock_initial_nodes
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.baremetal,
keystone_client=None)
self.assertEqual([], self.baremetal.node.updates)
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
def test_available_does_not_require_api_1_11(self, mock_register_nodes):
arglist = [self.json_file.name, '--json', '-s', 'http://localhost']
verifylist = [
('csv', False),
('json', True),
]
self.baremetal.http_client.os_ironic_api_version = '1.6'
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_register_nodes.return_value = self.mock_initial_nodes
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.baremetal,
keystone_client=None)
self.assertEqual(sorted([
('ABCDEFGH', 'manage'), ('IJKLMNOP', 'manage'),
('ABCDEFGH', 'provide'), ('IJKLMNOP', 'provide')
]), sorted(self.baremetal.node.updates))
def test_enroll_requires_api_1_11(self):
arglist = [
self.json_file.name,
'--json',
'-s', 'http://localhost',
'--initial-state', 'enroll'
]
verifylist = [
('csv', False),
('json', True),
]
self.baremetal.http_client.os_ironic_api_version = '1.6'
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaisesRegexp(exceptions.InvalidConfiguration,
'OS_BAREMETAL_API_VERSION',
self.cmd.take_action, parsed_args)
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
def test_json_import_detect_suffix(self, mock_register_nodes):
@@ -371,7 +459,7 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
@@ -385,13 +473,18 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_register_nodes.return_value = self.mock_initial_nodes
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
self.assertEqual(sorted([
('ABCDEFGH', 'manage'), ('IJKLMNOP', 'manage'),
('ABCDEFGH', 'provide'), ('IJKLMNOP', 'provide')
]), sorted(self.baremetal.node.updates))
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
def test_csv_import(self, mock_register_nodes):
@@ -404,12 +497,13 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_register_nodes.return_value = self.mock_initial_nodes
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
@@ -428,7 +522,7 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
@mock.patch('tripleo_common.utils.nodes.register_all_nodes', autospec=True)
@@ -447,7 +541,7 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
def test_invalid_import_filetype(self):
@@ -476,13 +570,18 @@ pxe_ssh,192.168.122.2,stack,"KEY2",00:0b:d0:69:7e:58""")
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_register_nodes.return_value = self.mock_initial_nodes
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost', self.nodes_list,
client=self.app.client_manager.baremetal,
client=self.baremetal,
keystone_client=None)
self.assertEqual(sorted([
('ABCDEFGH', 'manage'), ('IJKLMNOP', 'manage'),
('ABCDEFGH', 'provide'), ('IJKLMNOP', 'provide')
]), sorted(self.baremetal.node.updates))
@mock.patch('time.sleep', lambda sec: None)

View File

@@ -475,7 +475,7 @@ def set_nodes_state(baremetal_client, nodes, transition, target_state,
continue
log.debug(
"Setting provision state from {0} to '{1} for Node {2}"
"Setting provision state from '{0}' to '{1}' for Node {2}"
.format(node.provision_state, transition, node.uuid))
baremetal_client.node.set_provision_state(node.uuid, transition)

View File

@@ -159,6 +159,13 @@ class ImportBaremetal(command.Command):
'--csv', dest='csv', action='store_true',
help=_('Deprecated, now detected via file extension.'))
parser.add_argument('file_in', type=argparse.FileType('r'))
parser.add_argument(
'--initial-state',
choices=['enroll', 'available'],
default='available',
help='Provision state for newly-enrolled nodes.'
)
return parser
def take_action(self, parsed_args):
@@ -179,12 +186,32 @@ class ImportBaremetal(command.Command):
if 'nodes' in nodes_config:
nodes_config = nodes_config['nodes']
nodes.register_all_nodes(
client = self.app.client_manager.baremetal
if parsed_args.initial_state == "enroll":
api_version = client.http_client.os_ironic_api_version
if [int(part) for part in api_version.split('.')] < [1, 11]:
raise exceptions.InvalidConfiguration(
_("OS_BAREMETAL_API_VERSION must be >=1.11 for use of "
"'enroll' provision state; currently %s") % api_version)
new_nodes = nodes.register_all_nodes(
parsed_args.service_host,
nodes_config,
client=self.app.client_manager.baremetal,
client=client,
keystone_client=self.app.client_manager.identity)
if parsed_args.initial_state == "available":
manageable_node_uuids = list(utils.set_nodes_state(
client, new_nodes, "manage", "manageable",
skipped_states={'manageable', 'available'}
))
manageable_nodes = [
n for n in new_nodes if n.uuid in manageable_node_uuids
]
list(utils.set_nodes_state(
client, manageable_nodes, "provide", "available",
skipped_states={'available'}
))
class StartBaremetalIntrospectionBulk(command.Command):
"""Start bulk introspection on all baremetal nodes"""