Move baremetal provide commands from Ansible
Currently, we leverage Ansible to handle the baremetal provide process. As part of our efforts to support work on Tripleo.Next, these Ansible workflows will need to be migrated to tripleoclient. This change is consolodating Python methods from tripleo-ansible into tripleoclient. Change-Id: I1ff12506d4f6a7d5868aac7128b9f9dc3b8893ff
This commit is contained in:
parent
2ac3953410
commit
3ac32b592c
@ -143,3 +143,10 @@ class HeatPodMessageQueueException(Base):
|
|||||||
|
|
||||||
class InvalidPlaybook(Base):
|
class InvalidPlaybook(Base):
|
||||||
"""Invalid playbook path specified"""
|
"""Invalid playbook path specified"""
|
||||||
|
|
||||||
|
|
||||||
|
class NoNodeFound(Base):
|
||||||
|
"""No nodes matching specifications found"""
|
||||||
|
def __init__(self):
|
||||||
|
message = "No nodes matching specifications could be found. "
|
||||||
|
super(NoNodeFound, self).__init__(message)
|
||||||
|
@ -422,3 +422,24 @@ class FakeFlavor(object):
|
|||||||
'capabilities:boot_option': 'local',
|
'capabilities:boot_option': 'local',
|
||||||
'capabilities:profile': self.profile
|
'capabilities:profile': self.profile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeMachine:
|
||||||
|
def __init__(self, id, name=None, driver=None, driver_info=None,
|
||||||
|
chassis_uuid=None, instance_info=None, instance_uuid=None,
|
||||||
|
properties=None, reservation=None, last_error=None,
|
||||||
|
provision_state='available', is_maintenance=False,
|
||||||
|
power_state='power off'):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.driver = driver
|
||||||
|
self.driver_info = driver_info
|
||||||
|
self.chassis_uuid = chassis_uuid
|
||||||
|
self.instance_info = instance_info
|
||||||
|
self.instance_uuid = instance_uuid
|
||||||
|
self.properties = properties
|
||||||
|
self.reservation = reservation
|
||||||
|
self.last_error = last_error
|
||||||
|
self.provision_state = provision_state
|
||||||
|
self.is_maintenance = is_maintenance
|
||||||
|
self.power_state = power_state
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
import uuid
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@ -35,3 +36,12 @@ class TestOvercloudNode(fakes.FakePlaybookExecution):
|
|||||||
)
|
)
|
||||||
self.mock_playbook.start()
|
self.mock_playbook.start()
|
||||||
self.addCleanup(self.mock_playbook.stop)
|
self.addCleanup(self.mock_playbook.stop)
|
||||||
|
|
||||||
|
|
||||||
|
def make_fake_machine(machine_name, provision_state='manageable',
|
||||||
|
is_maintenance=False, machine_id=None):
|
||||||
|
if not machine_id:
|
||||||
|
machine_id = uuid.uuid4().hex
|
||||||
|
return(fakes.FakeMachine(id=machine_id, name=machine_name,
|
||||||
|
provision_state=provision_state,
|
||||||
|
is_maintenance=is_maintenance))
|
||||||
|
@ -21,6 +21,8 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
import openstack
|
||||||
|
|
||||||
from osc_lib import exceptions as oscexc
|
from osc_lib import exceptions as oscexc
|
||||||
from osc_lib.tests import utils as test_utils
|
from osc_lib.tests import utils as test_utils
|
||||||
import yaml
|
import yaml
|
||||||
@ -375,40 +377,96 @@ class TestDeleteNode(fakes.TestDeleteNode):
|
|||||||
mock_warning.assert_called_once_with(expected_message)
|
mock_warning.assert_called_once_with(expected_message)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(openstack.baremetal.v1._proxy, 'Proxy',
|
||||||
|
autospec=True, name='mock_bm')
|
||||||
|
@mock.patch('openstack.config', autospec=True,
|
||||||
|
name='mock_conf')
|
||||||
|
@mock.patch('openstack.connect', autospec=True,
|
||||||
|
name='mock_connect')
|
||||||
|
@mock.patch.object(openstack.connection,
|
||||||
|
'Connection', autospec=True)
|
||||||
class TestProvideNode(fakes.TestOvercloudNode):
|
class TestProvideNode(fakes.TestOvercloudNode):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestProvideNode, self).setUp()
|
super(TestProvideNode, self).setUp()
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = overcloud_node.ProvideNode(self.app, None)
|
self.cmd = overcloud_node.ProvideNode(self.app, None)
|
||||||
|
|
||||||
def test_provide_all_manageable_nodes(self):
|
iterate_timeout = mock.MagicMock()
|
||||||
|
iterate_timeout.start()
|
||||||
|
|
||||||
|
self.fake_baremetal_node = fakes.make_fake_machine(
|
||||||
|
machine_name='node1',
|
||||||
|
machine_id='4e540e11-1366-4b57-85d5-319d168d98a1'
|
||||||
|
)
|
||||||
|
self.fake_baremetal_node2 = fakes.make_fake_machine(
|
||||||
|
machine_name='node2',
|
||||||
|
machine_id='9070e42d-1ad7-4bd0-b868-5418bc9c7176'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_provide_all_manageable_nodes(self, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
|
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
|
||||||
|
mock_bm.baremetal.nodes.side_effect = [
|
||||||
|
iter([self.fake_baremetal_node]),
|
||||||
|
iter([self.fake_baremetal_node2])
|
||||||
|
]
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--all-manageable'],
|
['--all-manageable'],
|
||||||
[('all_manageable', True)])
|
[('all_manageable', True)])
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
def test_provide_one_node(self):
|
def test_provide_one_node(self, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
node_id = 'node_uuid1'
|
node_id = 'node_uuid1'
|
||||||
|
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[node_id],
|
[node_id],
|
||||||
[('node_uuids', [node_id])])
|
[('node_uuids', [node_id])])
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
def test_provide_multiple_nodes(self):
|
def test_provide_multiple_nodes(self, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
node_id1 = 'node_uuid1'
|
node_id1 = 'node_uuid1'
|
||||||
node_id2 = 'node_uuid2'
|
node_id2 = 'node_uuid2'
|
||||||
|
|
||||||
argslist = [node_id1, node_id2]
|
argslist = [node_id1, node_id2]
|
||||||
verifylist = [('node_uuids', [node_id1, node_id2])]
|
verifylist = [('node_uuids', [node_id1, node_id2])]
|
||||||
|
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2
|
||||||
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(openstack.baremetal.v1._proxy, 'Proxy',
|
||||||
|
autospec=True, name='mock_bm')
|
||||||
|
@mock.patch('openstack.config', autospec=True,
|
||||||
|
name='mock_conf')
|
||||||
|
@mock.patch('openstack.connect', autospec=True,
|
||||||
|
name='mock_connect')
|
||||||
|
@mock.patch.object(openstack.connection,
|
||||||
|
'Connection', autospec=True)
|
||||||
class TestCleanNode(fakes.TestOvercloudNode):
|
class TestCleanNode(fakes.TestOvercloudNode):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -417,41 +475,102 @@ class TestCleanNode(fakes.TestOvercloudNode):
|
|||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = overcloud_node.CleanNode(self.app, None)
|
self.cmd = overcloud_node.CleanNode(self.app, None)
|
||||||
|
|
||||||
def _check_clean_all_manageable(self, parsed_args, provide=False):
|
self.fake_baremetal_node = fakes.make_fake_machine(
|
||||||
|
machine_name='node1',
|
||||||
|
machine_id='4e540e11-1366-4b57-85d5-319d168d98a1'
|
||||||
|
)
|
||||||
|
self.fake_baremetal_node2 = fakes.make_fake_machine(
|
||||||
|
machine_name='node2',
|
||||||
|
machine_id='9070e42d-1ad7-4bd0-b868-5418bc9c7176'
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check_clean_all_manageable(self, parsed_args, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
provide=False):
|
||||||
|
mock_bm.baremetal.nodes.side_effect = [
|
||||||
|
iter([self.fake_baremetal_node]),
|
||||||
|
iter([self.fake_baremetal_node])
|
||||||
|
]
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node]
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
def _check_clean_nodes(self, parsed_args, nodes, provide=False):
|
def _check_clean_nodes(self, parsed_args, nodes, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm, provide=False):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
def test_clean_all_manageable_nodes_without_provide(self):
|
def test_clean_all_manageable_nodes_without_provide(self, mock_conn,
|
||||||
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm):
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal.nodes.return_value = iter([
|
||||||
|
self.fake_baremetal_node
|
||||||
|
])
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--all-manageable'],
|
['--all-manageable'],
|
||||||
[('all_manageable', True)])
|
[('all_manageable', True)])
|
||||||
self._check_clean_all_manageable(parsed_args, provide=False)
|
self._check_clean_all_manageable(parsed_args, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm, provide=False)
|
||||||
|
|
||||||
def test_clean_all_manageable_nodes_with_provide(self):
|
def test_clean_all_manageable_nodes_with_provide(self, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal.nodes.side_effect = [
|
||||||
|
iter([self.fake_baremetal_node]),
|
||||||
|
iter([self.fake_baremetal_node])]
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node]
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--all-manageable', '--provide'],
|
['--all-manageable', '--provide'],
|
||||||
[('all_manageable', True),
|
[('all_manageable', True),
|
||||||
('provide', True)])
|
('provide', True)])
|
||||||
self._check_clean_all_manageable(parsed_args, provide=True)
|
self._check_clean_all_manageable(parsed_args, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm, provide=False)
|
||||||
|
|
||||||
def test_clean_nodes_without_provide(self):
|
def test_clean_nodes_without_provide(self, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
nodes = ['node_uuid1', 'node_uuid2']
|
nodes = ['node_uuid1', 'node_uuid2']
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
nodes,
|
nodes,
|
||||||
[('node_uuids', nodes)])
|
[('node_uuids', nodes)])
|
||||||
self._check_clean_nodes(parsed_args, nodes, provide=False)
|
self._check_clean_nodes(parsed_args, nodes, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm, provide=False)
|
||||||
|
|
||||||
|
def test_clean_nodes_with_provide(self, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
|
||||||
def test_clean_nodes_with_provide(self):
|
|
||||||
nodes = ['node_uuid1', 'node_uuid2']
|
nodes = ['node_uuid1', 'node_uuid2']
|
||||||
argslist = nodes + ['--provide']
|
argslist = nodes + ['--provide']
|
||||||
|
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2
|
||||||
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
argslist,
|
argslist,
|
||||||
[('node_uuids', nodes),
|
[('node_uuids', nodes),
|
||||||
('provide', True)])
|
('provide', True)])
|
||||||
self._check_clean_nodes(parsed_args, nodes, provide=True)
|
self._check_clean_nodes(parsed_args, nodes, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm, provide=False)
|
||||||
|
|
||||||
|
|
||||||
class TestImportNodeMultiArch(fakes.TestOvercloudNode):
|
class TestImportNodeMultiArch(fakes.TestOvercloudNode):
|
||||||
@ -673,6 +792,17 @@ class TestConfigureNode(fakes.TestOvercloudNode):
|
|||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(openstack.baremetal.v1._proxy, 'Proxy', autospec=True,
|
||||||
|
name="mock_bm")
|
||||||
|
@mock.patch('openstack.config', autospec=True, name='mock_conf')
|
||||||
|
@mock.patch('openstack.connect', autospec=True, name='mock_connect')
|
||||||
|
@mock.patch.object(openstack.connection, 'Connection', autospec=True)
|
||||||
|
@mock.patch('tripleo_common.utils.nodes._populate_node_mapping',
|
||||||
|
name='mock_nodemap')
|
||||||
|
@mock.patch('tripleo_common.utils.nodes.register_all_nodes',
|
||||||
|
name='mock_tcnode')
|
||||||
|
@mock.patch('oslo_concurrency.processutils.execute',
|
||||||
|
name="mock_subproc")
|
||||||
class TestDiscoverNode(fakes.TestOvercloudNode):
|
class TestDiscoverNode(fakes.TestOvercloudNode):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -688,8 +818,19 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
|
|||||||
self.addCleanup(self.gcn.stop)
|
self.addCleanup(self.gcn.stop)
|
||||||
|
|
||||||
self.http_boot = '/var/lib/ironic/httpboot'
|
self.http_boot = '/var/lib/ironic/httpboot'
|
||||||
|
self.fake_baremetal_node = fakes.make_fake_machine(
|
||||||
|
machine_name='node1',
|
||||||
|
machine_id='4e540e11-1366-4b57-85d5-319d168d98a1'
|
||||||
|
)
|
||||||
|
self.fake_baremetal_node2 = fakes.make_fake_machine(
|
||||||
|
machine_name='node2',
|
||||||
|
machine_id='9070e42d-1ad7-4bd0-b868-5418bc9c7176'
|
||||||
|
)
|
||||||
|
|
||||||
def test_with_ip_range(self):
|
def test_with_ip_range(self, mock_subproc, mock_tcnode,
|
||||||
|
mock_nodemap, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
argslist = ['--range', '10.0.0.0/24',
|
argslist = ['--range', '10.0.0.0/24',
|
||||||
'--credentials', 'admin:password']
|
'--credentials', 'admin:password']
|
||||||
verifylist = [('ip_addresses', '10.0.0.0/24'),
|
verifylist = [('ip_addresses', '10.0.0.0/24'),
|
||||||
@ -698,7 +839,10 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
|
|||||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
def test_with_address_list(self):
|
def test_with_address_list(self, mock_subproc, mock_tcnode,
|
||||||
|
mock_nodemap, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
argslist = ['--ip', '10.0.0.1', '--ip', '10.0.0.2',
|
argslist = ['--ip', '10.0.0.1', '--ip', '10.0.0.2',
|
||||||
'--credentials', 'admin:password']
|
'--credentials', 'admin:password']
|
||||||
verifylist = [('ip_addresses', ['10.0.0.1', '10.0.0.2']),
|
verifylist = [('ip_addresses', ['10.0.0.1', '10.0.0.2']),
|
||||||
@ -707,7 +851,18 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
|
|||||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
def test_with_all_options(self):
|
def test_with_all_options(self, mock_subproc, mock_tcnode,
|
||||||
|
mock_nodemap, mock_conn,
|
||||||
|
mock_connect, mock_conf,
|
||||||
|
mock_bm):
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2,
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2
|
||||||
|
]
|
||||||
argslist = ['--range', '10.0.0.0/24',
|
argslist = ['--range', '10.0.0.0/24',
|
||||||
'--credentials', 'admin:password',
|
'--credentials', 'admin:password',
|
||||||
'--credentials', 'admin2:password2',
|
'--credentials', 'admin2:password2',
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
import uuid
|
||||||
|
|
||||||
from tripleoclient.tests import fakes
|
from tripleoclient.tests import fakes
|
||||||
|
|
||||||
@ -26,3 +27,12 @@ class TestOvercloudNode(fakes.FakePlaybookExecution):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestOvercloudNode, self).setUp()
|
super(TestOvercloudNode, self).setUp()
|
||||||
|
|
||||||
|
|
||||||
|
def make_fake_machine(machine_name, provision_state,
|
||||||
|
is_maintenance, machine_id=None):
|
||||||
|
if not machine_id:
|
||||||
|
machine_id = uuid.uuid4().hex
|
||||||
|
return(fakes.FakeMachine(id=machine_id, name=machine_name,
|
||||||
|
provision_state=provision_state,
|
||||||
|
is_maintenance=is_maintenance))
|
||||||
|
@ -20,13 +20,22 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
import openstack
|
||||||
from osc_lib.tests import utils as test_utils
|
from osc_lib.tests import utils as test_utils
|
||||||
|
|
||||||
from tripleoclient import constants
|
from tripleoclient import constants
|
||||||
from tripleoclient.tests.v2.overcloud_node import fakes
|
from tripleoclient.tests.v2.overcloud_node import fakes
|
||||||
from tripleoclient.v2 import overcloud_node
|
from tripleoclient.v2 import overcloud_node
|
||||||
|
from tripleoclient.workflows import tripleo_baremetal as tb
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(openstack.baremetal.v1._proxy, 'Proxy', autospec=True,
|
||||||
|
name="mock_bm")
|
||||||
|
@mock.patch('openstack.config', autospec=True, name='mock_conf')
|
||||||
|
@mock.patch('openstack.connect', autospec=True, name='mock_connect')
|
||||||
|
@mock.patch.object(openstack.connection, 'Connection', autospec=True)
|
||||||
class TestImportNode(fakes.TestOvercloudNode):
|
class TestImportNode(fakes.TestOvercloudNode):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -49,6 +58,18 @@ class TestImportNode(fakes.TestOvercloudNode):
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
self.fake_baremetal_node = fakes.make_fake_machine(
|
||||||
|
machine_name='node1',
|
||||||
|
machine_id='4e540e11-1366-4b57-85d5-319d168d98a1',
|
||||||
|
provision_state='manageable',
|
||||||
|
is_maintenance=False
|
||||||
|
)
|
||||||
|
self.fake_baremetal_node2 = fakes.make_fake_machine(
|
||||||
|
machine_name='node2',
|
||||||
|
machine_id='9070e42d-1ad7-4bd0-b868-5418bc9c7176',
|
||||||
|
provision_state='manageable',
|
||||||
|
is_maintenance=False
|
||||||
|
)
|
||||||
self.json_file = tempfile.NamedTemporaryFile(
|
self.json_file = tempfile.NamedTemporaryFile(
|
||||||
mode='w', delete=False, suffix='.json')
|
mode='w', delete=False, suffix='.json')
|
||||||
json.dump(self.nodes_list, self.json_file)
|
json.dump(self.nodes_list, self.json_file)
|
||||||
@ -72,18 +93,24 @@ class TestImportNode(fakes.TestOvercloudNode):
|
|||||||
for i in ('agent.kernel',
|
for i in ('agent.kernel',
|
||||||
'agent.ramdisk')]))
|
'agent.ramdisk')]))
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_import_only(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_import_only(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[self.json_file.name],
|
[self.json_file.name],
|
||||||
[('introspect', False),
|
[('introspect', False),
|
||||||
('provide', False)])
|
('provide', False)])
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_import_and_introspect(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_import_and_introspect(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[self.json_file.name,
|
[self.json_file.name,
|
||||||
'--introspect'],
|
'--introspect'],
|
||||||
@ -103,25 +130,45 @@ class TestImportNode(fakes.TestOvercloudNode):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_import_and_provide(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_import_and_provide(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[self.json_file.name,
|
[self.json_file.name,
|
||||||
'--provide'],
|
'--provide'],
|
||||||
[('introspect', False),
|
[('introspect', False),
|
||||||
('provide', True)])
|
('provide', True)])
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal_introspection = mock_bm
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2]
|
||||||
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_import_and_introspect_and_provide(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_import_and_introspect_and_provide(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[self.json_file.name,
|
[self.json_file.name,
|
||||||
'--introspect',
|
'--introspect',
|
||||||
'--provide'],
|
'--provide'],
|
||||||
[('introspect', True),
|
[('introspect', True),
|
||||||
('provide', True)])
|
('provide', True)])
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal_introspection = mock_bm
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2]
|
||||||
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_playbook.assert_called_with(
|
mock_playbook.assert_called_with(
|
||||||
workdir=mock.ANY,
|
workdir=mock.ANY,
|
||||||
@ -130,22 +177,30 @@ class TestImportNode(fakes.TestOvercloudNode):
|
|||||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||||
verbosity=mock.ANY,
|
verbosity=mock.ANY,
|
||||||
extra_vars={
|
extra_vars={
|
||||||
'node_uuids': ['MOCK_NODE_UUID']
|
'node_uuids': ['MOCK_NODE_UUID'],
|
||||||
|
'run_validations': False,
|
||||||
|
'concurrency': 20
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_import_with_netboot(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_import_with_netboot(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[self.json_file.name,
|
[self.json_file.name,
|
||||||
'--no-deploy-image'],
|
'--no-deploy-image'],
|
||||||
[('no_deploy_image', True)])
|
[('no_deploy_image', True)])
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_import_with_no_deployed_image(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_import_with_no_deployed_image(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
[self.json_file.name,
|
[self.json_file.name,
|
||||||
'--instance-boot-option',
|
'--instance-boot-option',
|
||||||
@ -154,16 +209,37 @@ class TestImportNode(fakes.TestOvercloudNode):
|
|||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(openstack.baremetal.v1._proxy, 'Proxy', autospec=True,
|
||||||
|
name="mock_bm")
|
||||||
|
@mock.patch('openstack.config', autospec=True, name='mock_conf')
|
||||||
|
@mock.patch('openstack.connect', autospec=True, name='mock_connect')
|
||||||
|
@mock.patch.object(openstack.connection, 'Connection', autospec=True)
|
||||||
class TestIntrospectNode(fakes.TestOvercloudNode):
|
class TestIntrospectNode(fakes.TestOvercloudNode):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestIntrospectNode, self).setUp()
|
super(TestIntrospectNode, self).setUp()
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = overcloud_node.IntrospectNode(self.app, None)
|
self.cmd = overcloud_node.IntrospectNode(self.app, None)
|
||||||
|
self.fake_baremetal_node = fakes.make_fake_machine(
|
||||||
|
machine_name='node1',
|
||||||
|
machine_id='4e540e11-1366-4b57-85d5-319d168d98a1',
|
||||||
|
provision_state='manageable',
|
||||||
|
is_maintenance=False
|
||||||
|
)
|
||||||
|
self.fake_baremetal_node2 = fakes.make_fake_machine(
|
||||||
|
machine_name='node2',
|
||||||
|
machine_id='9070e42d-1ad7-4bd0-b868-5418bc9c7176',
|
||||||
|
provision_state='manageable',
|
||||||
|
is_maintenance=False
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
|
||||||
autospec=True)
|
|
||||||
def test_introspect_all_manageable_nodes_without_provide(self,
|
def test_introspect_all_manageable_nodes_without_provide(self,
|
||||||
|
mock_conn,
|
||||||
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
mock_playbook):
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--all-manageable'],
|
['--all-manageable'],
|
||||||
@ -185,29 +261,54 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
|
||||||
autospec=True)
|
|
||||||
def test_introspect_all_manageable_nodes_with_provide(self,
|
def test_introspect_all_manageable_nodes_with_provide(self,
|
||||||
|
mock_conn,
|
||||||
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
mock_playbook):
|
mock_playbook):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--all-manageable', '--provide'],
|
['--all-manageable', '--provide'],
|
||||||
[('all_manageable', True),
|
[('all_manageable', True),
|
||||||
('provide', True)])
|
('provide', True)])
|
||||||
|
tb.TripleoProvide.provide = mock.MagicMock()
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal.nodes.side_effect = [
|
||||||
|
iter([self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2]),
|
||||||
|
iter([self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2])
|
||||||
|
]
|
||||||
|
|
||||||
|
expected_nodes = ['4e540e11-1366-4b57-85d5-319d168d98a1',
|
||||||
|
'9070e42d-1ad7-4bd0-b868-5418bc9c7176']
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_playbook.assert_called_with(
|
mock_playbook.assert_called_with(
|
||||||
workdir=mock.ANY,
|
workdir=mock.ANY,
|
||||||
playbook='cli-overcloud-node-provide.yaml',
|
playbook='cli-baremetal-introspect.yaml',
|
||||||
inventory=mock.ANY,
|
inventory=mock.ANY,
|
||||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||||
verbosity=mock.ANY,
|
verbosity=mock.ANY,
|
||||||
extra_vars={
|
extra_vars={
|
||||||
'node_uuids': []
|
'node_uuids': [],
|
||||||
|
'run_validations': False,
|
||||||
|
'concurrency': 20,
|
||||||
|
'node_timeout': 1200,
|
||||||
|
'max_retries': 1,
|
||||||
|
'retry_timeout': 120,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
tb.TripleoProvide.provide.assert_called_with(
|
||||||
autospec=True)
|
expected_nodes)
|
||||||
def test_introspect_nodes_without_provide(self, mock_playbook):
|
|
||||||
|
def test_introspect_nodes_without_provide(self,
|
||||||
|
mock_conn,
|
||||||
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
nodes = ['node_uuid1', 'node_uuid2']
|
nodes = ['node_uuid1', 'node_uuid2']
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
nodes,
|
nodes,
|
||||||
@ -229,37 +330,48 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_introspect_nodes_with_provide(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_introspect_nodes_with_provide(self, mock_playbook):
|
mock_connect,
|
||||||
nodes = ['node_uuid1', 'node_uuid2']
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
|
nodes = ['node1', 'node2']
|
||||||
argslist = nodes + ['--provide']
|
argslist = nodes + ['--provide']
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
argslist,
|
argslist,
|
||||||
[('node_uuids', nodes),
|
[('node_uuids', nodes),
|
||||||
('provide', True)])
|
('provide', True)])
|
||||||
|
tb.TripleoProvide.provide = mock.MagicMock()
|
||||||
|
mock_conn.return_value = mock_bm
|
||||||
|
mock_bm.baremetal = mock_bm
|
||||||
|
mock_bm.baremetal_introspection = mock_bm
|
||||||
|
mock_bm.baremetal.get_node.side_effect = [
|
||||||
|
self.fake_baremetal_node,
|
||||||
|
self.fake_baremetal_node2]
|
||||||
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_playbook.assert_called_with(
|
|
||||||
workdir=mock.ANY,
|
tb.TripleoProvide.provide.assert_called_with(
|
||||||
playbook='cli-overcloud-node-provide.yaml',
|
nodes=nodes
|
||||||
inventory=mock.ANY,
|
|
||||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
|
||||||
verbosity=mock.ANY,
|
|
||||||
extra_vars={
|
|
||||||
'node_uuids': nodes
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_introspect_no_node_or_flag_specified(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_introspect_no_node_or_flag_specified(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
self.assertRaises(test_utils.ParserException,
|
self.assertRaises(test_utils.ParserException,
|
||||||
self.check_parser,
|
self.check_parser,
|
||||||
self.cmd, [], [])
|
self.cmd, [], [])
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
def test_introspect_uuids_and_all_both_specified(self,
|
||||||
autospec=True)
|
mock_conn,
|
||||||
def test_introspect_uuids_and_all_both_specified(self, mock_playbook):
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook):
|
||||||
argslist = ['node_id1', 'node_id2', '--all-manageable']
|
argslist = ['node_id1', 'node_id2', '--all-manageable']
|
||||||
verifylist = [('node_uuids', ['node_id1', 'node_id2']),
|
verifylist = [('node_uuids', ['node_id1', 'node_id2']),
|
||||||
('all_manageable', True)]
|
('all_manageable', True)]
|
||||||
@ -267,7 +379,12 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
|
|||||||
self.check_parser,
|
self.check_parser,
|
||||||
self.cmd, argslist, verifylist)
|
self.cmd, argslist, verifylist)
|
||||||
|
|
||||||
def _check_introspect_all_manageable(self, parsed_args, provide=False):
|
def _check_introspect_all_manageable(self, parsed_args,
|
||||||
|
mock_conn,
|
||||||
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook, provide=False):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
call_list = [mock.call(
|
call_list = [mock.call(
|
||||||
@ -285,7 +402,12 @@ class TestIntrospectNode(fakes.TestOvercloudNode):
|
|||||||
self.assertEqual(self.workflow.executions.create.call_count,
|
self.assertEqual(self.workflow.executions.create.call_count,
|
||||||
2 if provide else 1)
|
2 if provide else 1)
|
||||||
|
|
||||||
def _check_introspect_nodes(self, parsed_args, nodes, provide=False):
|
def _check_introspect_nodes(self, parsed_args, nodes,
|
||||||
|
mock_conn,
|
||||||
|
mock_connect,
|
||||||
|
mock_conf,
|
||||||
|
mock_bm,
|
||||||
|
mock_playbook, provide=False):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
call_list = [mock.call(
|
call_list = [mock.call(
|
||||||
|
@ -96,9 +96,6 @@ class TestBaremetalWorkflows(fakes.FakePlaybookExecution):
|
|||||||
instance_boot_option='local'
|
instance_boot_option='local'
|
||||||
), [mock.ANY])
|
), [mock.ANY])
|
||||||
|
|
||||||
def test_provide_success(self):
|
|
||||||
baremetal.provide(node_uuids=[])
|
|
||||||
|
|
||||||
def test_introspect_success(self):
|
def test_introspect_success(self):
|
||||||
baremetal.introspect(self.app.client_manager, node_uuids=[],
|
baremetal.introspect(self.app.client_manager, node_uuids=[],
|
||||||
run_validations=True, concurrency=20,
|
run_validations=True, concurrency=20,
|
||||||
@ -111,11 +108,6 @@ class TestBaremetalWorkflows(fakes.FakePlaybookExecution):
|
|||||||
node_timeout=1200, max_retries=1, retry_timeout=120,
|
node_timeout=1200, max_retries=1, retry_timeout=120,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_provide_manageable_nodes_success(self):
|
|
||||||
baremetal.provide_manageable_nodes(
|
|
||||||
self.app.client_manager
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_configure_success(self):
|
def test_configure_success(self):
|
||||||
baremetal.configure(self.app.client_manager, node_uuids=[])
|
baremetal.configure(self.app.client_manager, node_uuids=[])
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ from tripleoclient import command
|
|||||||
from tripleoclient import constants
|
from tripleoclient import constants
|
||||||
from tripleoclient import utils as oooutils
|
from tripleoclient import utils as oooutils
|
||||||
from tripleoclient.workflows import baremetal
|
from tripleoclient.workflows import baremetal
|
||||||
|
from tripleoclient.workflows import tripleo_baremetal as tb
|
||||||
|
|
||||||
|
|
||||||
class DeleteNode(command.Command):
|
class DeleteNode(command.Command):
|
||||||
@ -214,19 +215,22 @@ class ProvideNode(command.Command):
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help=_("Provide all nodes currently in 'manageable'"
|
help=_("Provide all nodes currently in 'manageable'"
|
||||||
" state"))
|
" state"))
|
||||||
|
group.add_argument("--verbosity",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help=_("Print debug output during execution"))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)" % parsed_args)
|
self.log.debug("take_action(%s)" % parsed_args)
|
||||||
|
|
||||||
|
provide = tb.TripleoProvide(verbosity=parsed_args.verbosity)
|
||||||
|
|
||||||
if parsed_args.node_uuids:
|
if parsed_args.node_uuids:
|
||||||
baremetal.provide(node_uuids=parsed_args.node_uuids,
|
provide.provide(nodes=parsed_args.node_uuids)
|
||||||
verbosity=oooutils.playbook_verbosity(self))
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
baremetal.provide_manageable_nodes(
|
provide.provide_manageable_nodes()
|
||||||
self.app.client_manager,
|
|
||||||
verbosity=oooutils.playbook_verbosity(self))
|
|
||||||
|
|
||||||
|
|
||||||
class CleanNode(command.Command):
|
class CleanNode(command.Command):
|
||||||
@ -247,6 +251,10 @@ class CleanNode(command.Command):
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help=_("Clean all nodes currently in 'manageable'"
|
help=_("Clean all nodes currently in 'manageable'"
|
||||||
" state"))
|
" state"))
|
||||||
|
group.add_argument("--verbosity",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help=_("Print debug output during execution"))
|
||||||
parser.add_argument('--provide',
|
parser.add_argument('--provide',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help=_('Provide (make available) the nodes once '
|
help=_('Provide (make available) the nodes once '
|
||||||
@ -270,11 +278,11 @@ class CleanNode(command.Command):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.provide:
|
if parsed_args.provide:
|
||||||
|
provide = tb.TripleoProvide(verbosity=parsed_args.verbosity)
|
||||||
if nodes:
|
if nodes:
|
||||||
baremetal.provide(node_uuids=nodes,
|
provide.provide(nodes=nodes)
|
||||||
verbosity=oooutils.playbook_verbosity(self))
|
|
||||||
else:
|
else:
|
||||||
baremetal.provide_manageable_nodes(self.app.client_manager)
|
provide.provide_manageable_nodes()
|
||||||
|
|
||||||
|
|
||||||
class ConfigureNode(command.Command):
|
class ConfigureNode(command.Command):
|
||||||
@ -410,6 +418,10 @@ class DiscoverNode(command.Command):
|
|||||||
default=120,
|
default=120,
|
||||||
help=_('Maximum timeout between introspection'
|
help=_('Maximum timeout between introspection'
|
||||||
'retries'))
|
'retries'))
|
||||||
|
parser.add_argument("--verbosity",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help=_("Print debug output during execution"))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
# FIXME(tonyb): This is not multi-arch safe :(
|
# FIXME(tonyb): This is not multi-arch safe :(
|
||||||
@ -457,9 +469,8 @@ class DiscoverNode(command.Command):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.provide:
|
if parsed_args.provide:
|
||||||
baremetal.provide(
|
provide = tb.TripleoProvide(verbosity=parsed_args.verbosity)
|
||||||
node_uuids=nodes_uuids,
|
provide.provide(nodes=nodes_uuids)
|
||||||
verbosity=oooutils.playbook_verbosity(self))
|
|
||||||
|
|
||||||
|
|
||||||
class ExtractProvisionedNode(command.Command):
|
class ExtractProvisionedNode(command.Command):
|
||||||
|
@ -38,6 +38,7 @@ from tripleoclient.v1.overcloud_node import ConfigureNode # noqa
|
|||||||
from tripleoclient.v1.overcloud_node import DeleteNode # noqa
|
from tripleoclient.v1.overcloud_node import DeleteNode # noqa
|
||||||
from tripleoclient.v1.overcloud_node import DiscoverNode # noqa
|
from tripleoclient.v1.overcloud_node import DiscoverNode # noqa
|
||||||
from tripleoclient.v1.overcloud_node import ProvideNode # noqa
|
from tripleoclient.v1.overcloud_node import ProvideNode # noqa
|
||||||
|
from tripleoclient.workflows import tripleo_baremetal as tb
|
||||||
|
|
||||||
|
|
||||||
class ImportNode(command.Command):
|
class ImportNode(command.Command):
|
||||||
@ -87,6 +88,9 @@ class ImportNode(command.Command):
|
|||||||
default=20,
|
default=20,
|
||||||
help=_('Maximum number of nodes to introspect at '
|
help=_('Maximum number of nodes to introspect at '
|
||||||
'once.'))
|
'once.'))
|
||||||
|
parser.add_argument('--verbosity', type=int,
|
||||||
|
default=1,
|
||||||
|
help=_('Print debug logs during execution'))
|
||||||
parser.add_argument('env_file', type=argparse.FileType('r'))
|
parser.add_argument('env_file', type=argparse.FileType('r'))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -132,10 +136,8 @@ class ImportNode(command.Command):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.provide:
|
if parsed_args.provide:
|
||||||
baremetal.provide(
|
provide = tb.TripleoProvide(verbosity=parsed_args.verbosity)
|
||||||
verbosity=oooutils.playbook_verbosity(self=self),
|
provide.provide(nodes=nodes_uuids)
|
||||||
node_uuids=nodes_uuids
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class IntrospectNode(command.Command):
|
class IntrospectNode(command.Command):
|
||||||
@ -179,6 +181,9 @@ class IntrospectNode(command.Command):
|
|||||||
default=120,
|
default=120,
|
||||||
help=_('Maximum timeout between introspection'
|
help=_('Maximum timeout between introspection'
|
||||||
'retries'))
|
'retries'))
|
||||||
|
parser.add_argument('--verbosity', type=int,
|
||||||
|
default=1,
|
||||||
|
help=_('Print debug logs during execution'))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -209,16 +214,13 @@ class IntrospectNode(command.Command):
|
|||||||
# NOTE(cloudnull): This is using the old provide function, in a future
|
# NOTE(cloudnull): This is using the old provide function, in a future
|
||||||
# release this may be ported to a standalone playbook
|
# release this may be ported to a standalone playbook
|
||||||
if parsed_args.provide:
|
if parsed_args.provide:
|
||||||
|
provide = tb.TripleoProvide(verbosity=parsed_args.verbosity)
|
||||||
if parsed_args.node_uuids:
|
if parsed_args.node_uuids:
|
||||||
baremetal.provide(
|
provide.provide(
|
||||||
node_uuids=parsed_args.node_uuids,
|
nodes=parsed_args.node_uuids,
|
||||||
verbosity=oooutils.playbook_verbosity(self=self)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
baremetal.provide_manageable_nodes(
|
provide.provide_manageable_nodes()
|
||||||
clients=self.app.client_manager,
|
|
||||||
verbosity=oooutils.playbook_verbosity(self=self)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProvisionNode(command.Command):
|
class ProvisionNode(command.Command):
|
||||||
|
@ -105,50 +105,6 @@ def register_or_update(clients, nodes_json, kernel_name=None,
|
|||||||
return registered_nodes
|
return registered_nodes
|
||||||
|
|
||||||
|
|
||||||
def provide(node_uuids, verbosity=0):
|
|
||||||
"""Provide Baremetal Nodes
|
|
||||||
|
|
||||||
:param node_uuids: List of instance UUID(s).
|
|
||||||
:type node_uuids: List
|
|
||||||
|
|
||||||
:param verbosity: Verbosity level
|
|
||||||
:type verbosity: Integer
|
|
||||||
"""
|
|
||||||
|
|
||||||
with utils.TempDirs() as tmp:
|
|
||||||
utils.run_ansible_playbook(
|
|
||||||
playbook='cli-overcloud-node-provide.yaml',
|
|
||||||
inventory='localhost,',
|
|
||||||
workdir=tmp,
|
|
||||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
|
||||||
verbosity=verbosity,
|
|
||||||
extra_vars={
|
|
||||||
'node_uuids': node_uuids
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print('Successfully provided nodes: {}'.format(node_uuids))
|
|
||||||
|
|
||||||
|
|
||||||
def provide_manageable_nodes(clients, verbosity=0):
|
|
||||||
"""Provide all manageable Nodes
|
|
||||||
|
|
||||||
:param clients: Application client object.
|
|
||||||
:type clients: Object
|
|
||||||
|
|
||||||
:param verbosity: Verbosity level
|
|
||||||
:type verbosity: Integer
|
|
||||||
"""
|
|
||||||
|
|
||||||
provide(
|
|
||||||
node_uuids=[
|
|
||||||
i.uuid for i in clients.baremetal.node.list()
|
|
||||||
if i.provision_state == "manageable" and not i.maintenance
|
|
||||||
],
|
|
||||||
verbosity=verbosity
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def introspect(clients, node_uuids, run_validations, concurrency,
|
def introspect(clients, node_uuids, run_validations, concurrency,
|
||||||
node_timeout, max_retries, retry_timeout, verbosity=0):
|
node_timeout, max_retries, retry_timeout, verbosity=0):
|
||||||
"""Introspect Baremetal Nodes
|
"""Introspect Baremetal Nodes
|
||||||
|
177
tripleoclient/workflows/tripleo_baremetal.py
Normal file
177
tripleoclient/workflows/tripleo_baremetal.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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 logging
|
||||||
|
|
||||||
|
from openstack import connect as sdkclient
|
||||||
|
from openstack import exceptions
|
||||||
|
from openstack.utils import iterate_timeout
|
||||||
|
from tripleoclient import exceptions as ooo_exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class TripleoBaremetal(object):
|
||||||
|
|
||||||
|
"""Base class for TripleO Baremetal operations.
|
||||||
|
|
||||||
|
The TripleoBase class provides access to commonly used elements
|
||||||
|
required to interact with and perform baremetal operations for TripleO.
|
||||||
|
|
||||||
|
:param timeout: How long to wait until we consider this job to have
|
||||||
|
timed out
|
||||||
|
:type timeout: integer
|
||||||
|
|
||||||
|
:param verbosity: How verbose should we be. Currently, this just sets
|
||||||
|
DEBUG for any non-zero value provided.
|
||||||
|
:type verbosity: integer
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, timeout: int = 1200, verbosity: int = 1):
|
||||||
|
self.conn = sdkclient(
|
||||||
|
cloud='undercloud'
|
||||||
|
)
|
||||||
|
self.timeout = timeout
|
||||||
|
self.log = logging.getLogger(__name__)
|
||||||
|
if verbosity > 0:
|
||||||
|
self.log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
def all_manageable_nodes(self):
|
||||||
|
"""This method returns a list of manageable nodes from Ironic
|
||||||
|
|
||||||
|
We take no arguments and instead create a list of nodes that
|
||||||
|
are in the manageable state and NOT in maintenenace. We return the
|
||||||
|
subsequent list.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NoNodeFound: If no nodes match the above description, we will raise
|
||||||
|
an exception.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
nodes: The List of manageable nodes that are not currently in
|
||||||
|
maintenance.
|
||||||
|
"""
|
||||||
|
nodes = [n.id for n in self.conn.baremetal.nodes(
|
||||||
|
provision_state='manageable', is_maintenance=False)]
|
||||||
|
|
||||||
|
if not nodes:
|
||||||
|
raise ooo_exceptions.NoNodeFound
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
|
class TripleoProvide(TripleoBaremetal):
|
||||||
|
|
||||||
|
"""TripleoProvide handles state transition of baremetal nodes.
|
||||||
|
|
||||||
|
The TripleoProvide class handles the transition of nodes between the
|
||||||
|
manageable and available states.
|
||||||
|
|
||||||
|
:param wait_for_bridge_mapping: Bool to determine whether or not we are
|
||||||
|
waiting for the bridge mapping to be
|
||||||
|
active in ironic-neutron-agent
|
||||||
|
:type wait_for_bridge_mapping: bool
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, wait_for_bridge_mappings: bool = False,
|
||||||
|
verbosity: int = 1):
|
||||||
|
|
||||||
|
super().__init__(verbosity)
|
||||||
|
self.wait_for_bridge_mappings = wait_for_bridge_mappings
|
||||||
|
|
||||||
|
def _wait_for_unlocked(self, node: str, timeout: int):
|
||||||
|
timeout_msg = f'Timeout waiting for node {node} to be unlocked'
|
||||||
|
|
||||||
|
for count in iterate_timeout(timeout, timeout_msg):
|
||||||
|
node_info = self.conn.baremetal.get_node(
|
||||||
|
node,
|
||||||
|
fields=['reservation']
|
||||||
|
)
|
||||||
|
|
||||||
|
if node_info.reservation is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def _wait_for_bridge_mapping(self, node: str):
|
||||||
|
|
||||||
|
client = self.conn.network
|
||||||
|
timeout_msg = (f'Timeout waiting for node {node} to have '
|
||||||
|
'bridge_mappings set in the ironic-neutron-agent '
|
||||||
|
'entry')
|
||||||
|
|
||||||
|
# default agent polling period is 30s, so wait 60s
|
||||||
|
timeout = 60
|
||||||
|
|
||||||
|
for count in iterate_timeout(timeout, timeout_msg):
|
||||||
|
agents = list(
|
||||||
|
client.agents(host=node, binary='ironic-neutron-agent'))
|
||||||
|
|
||||||
|
if agents:
|
||||||
|
if agents[0].configuration.get('bridge_mappings'):
|
||||||
|
return
|
||||||
|
|
||||||
|
def provide(self, nodes: str):
|
||||||
|
|
||||||
|
"""Transition nodes to the Available state.
|
||||||
|
|
||||||
|
provide handles the state transition from the nodes current state
|
||||||
|
to the available state
|
||||||
|
|
||||||
|
:param nodes: The node UUID or name that we will be working on
|
||||||
|
:type nodes: String
|
||||||
|
"""
|
||||||
|
|
||||||
|
client = self.conn.baremetal
|
||||||
|
node_timeout = self.timeout
|
||||||
|
nodes_wait = nodes[:]
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
self.log.info('Providing node: {}'.format(node))
|
||||||
|
self._wait_for_unlocked(node, node_timeout)
|
||||||
|
|
||||||
|
if self.wait_for_bridge_mappings:
|
||||||
|
self._wait_for_bridge_mapping(node)
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.set_node_provision_state(
|
||||||
|
node,
|
||||||
|
"provide",
|
||||||
|
wait=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
nodes_wait.remove(node)
|
||||||
|
self.log.error(
|
||||||
|
"Can not start providing for node {}: {}".format(
|
||||||
|
nodes, e))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.log.info(
|
||||||
|
"Waiting for available state: {}".format(nodes_wait))
|
||||||
|
|
||||||
|
client.wait_for_nodes_provision_state(
|
||||||
|
nodes=nodes_wait,
|
||||||
|
expected_state='available',
|
||||||
|
timeout=self.timeout,
|
||||||
|
fail=False
|
||||||
|
)
|
||||||
|
|
||||||
|
except exceptions.ResourceFailure as e:
|
||||||
|
self.log.error("Failed providing nodes due to failure: {}".format(
|
||||||
|
e))
|
||||||
|
return
|
||||||
|
|
||||||
|
except exceptions.ResourceTimeout as e:
|
||||||
|
self.log.error("Failed providing nodes due to timeout: {}".format(
|
||||||
|
e))
|
||||||
|
|
||||||
|
def provide_manageable_nodes(self):
|
||||||
|
self.provide(self.all_manageable_nodes())
|
Loading…
Reference in New Issue
Block a user