Merge "Add action for generating fencing parameters."
This commit is contained in:
commit
5d977af0ac
@ -8,6 +8,8 @@ features:
|
|||||||
- Add a new Workflow which can be used to wait for Heat stacks finish with
|
- Add a new Workflow which can be used to wait for Heat stacks finish with
|
||||||
COMPLETE or FAILED.
|
COMPLETE or FAILED.
|
||||||
- CephMdsKey is now a generated Heat parameter.
|
- CephMdsKey is now a generated Heat parameter.
|
||||||
|
- Add an new Action which generates environment parameters for configuring
|
||||||
|
fencing.
|
||||||
fixes:
|
fixes:
|
||||||
- Fixes `bug 1644756 <https://bugs.launchpad.net/tripleo/+bug/1644756>`__ so
|
- Fixes `bug 1644756 <https://bugs.launchpad.net/tripleo/+bug/1644756>`__ so
|
||||||
that flavour matching works as expected with the object-storage role.
|
that flavour matching works as expected with the object-storage role.
|
||||||
|
@ -78,6 +78,7 @@ mistral.actions =
|
|||||||
tripleo.parameters.update_role = tripleo_common.actions.parameters:UpdateRoleParametersAction
|
tripleo.parameters.update_role = tripleo_common.actions.parameters:UpdateRoleParametersAction
|
||||||
tripleo.parameters.generate_passwords = tripleo_common.actions.parameters:GeneratePasswordsAction
|
tripleo.parameters.generate_passwords = tripleo_common.actions.parameters:GeneratePasswordsAction
|
||||||
tripleo.parameters.get_passwords = tripleo_common.actions.parameters:GetPasswordsAction
|
tripleo.parameters.get_passwords = tripleo_common.actions.parameters:GetPasswordsAction
|
||||||
|
tripleo.parameters.generate_fencing = tripleo_common.actions.parameters:GenerateFencingParametersAction
|
||||||
tripleo.plan.create = tripleo_common.actions.plan:CreatePlanAction
|
tripleo.plan.create = tripleo_common.actions.plan:CreatePlanAction
|
||||||
tripleo.plan.update = tripleo_common.actions.plan:UpdatePlanAction
|
tripleo.plan.update = tripleo_common.actions.plan:UpdatePlanAction
|
||||||
tripleo.plan.create_container = tripleo_common.actions.plan:CreateContainerAction
|
tripleo.plan.create_container = tripleo_common.actions.plan:CreateContainerAction
|
||||||
|
@ -34,6 +34,7 @@ from mistral.workflow import utils as mistral_workflow_utils
|
|||||||
from tripleo_common.actions import base
|
from tripleo_common.actions import base
|
||||||
from tripleo_common.actions import templates
|
from tripleo_common.actions import templates
|
||||||
from tripleo_common import constants
|
from tripleo_common import constants
|
||||||
|
from tripleo_common.utils import nodes
|
||||||
from tripleo_common.utils import parameters
|
from tripleo_common.utils import parameters
|
||||||
from tripleo_common.utils import passwords as password_utils
|
from tripleo_common.utils import passwords as password_utils
|
||||||
|
|
||||||
@ -210,3 +211,87 @@ class GetPasswordsAction(base.TripleOAction):
|
|||||||
passwords[name] = parameter_defaults[name]
|
passwords[name] = parameter_defaults[name]
|
||||||
|
|
||||||
return passwords
|
return passwords
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateFencingParametersAction(base.TripleOAction):
|
||||||
|
"""Generates fencing configuration for a deployment.
|
||||||
|
|
||||||
|
:param nodes_json: list of nodes & attributes in json format
|
||||||
|
:param os_auth: dictionary of OS client auth data (if using pxe_ssh)
|
||||||
|
:param fence_action: action to take when fencing nodes
|
||||||
|
:param delay: time to wait before taking fencing action
|
||||||
|
:param ipmi_level: IPMI user level to use
|
||||||
|
:param ipmi_cipher: IPMI cipher suite to use
|
||||||
|
:param ipmi_lanplus: whether to use IPMIv2.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, nodes_json, os_auth, fence_action, delay,
|
||||||
|
ipmi_level, ipmi_cipher, ipmi_lanplus):
|
||||||
|
super(GenerateFencingParametersAction, self).__init__()
|
||||||
|
self.nodes_json = nodes_json
|
||||||
|
self.os_auth = os_auth
|
||||||
|
self.fence_action = fence_action
|
||||||
|
self.delay = delay
|
||||||
|
self.ipmi_level = ipmi_level
|
||||||
|
self.ipmi_cipher = ipmi_cipher
|
||||||
|
self.ipmi_lanplus = ipmi_lanplus
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Returns the parameters for fencing controller nodes"""
|
||||||
|
hostmap = nodes.generate_hostmap(self.get_baremetal_client(),
|
||||||
|
self.get_compute_client())
|
||||||
|
fence_params = {"EnableFencing": True, "FencingConfig": {}}
|
||||||
|
devices = []
|
||||||
|
|
||||||
|
for node in self.nodes_json:
|
||||||
|
node_data = {}
|
||||||
|
params = {}
|
||||||
|
if "mac" in node:
|
||||||
|
# Not all Ironic drivers present a MAC address, so we only
|
||||||
|
# capture it if it's present
|
||||||
|
mac_addr = node["mac"][0]
|
||||||
|
node_data["host_mac"] = mac_addr
|
||||||
|
|
||||||
|
# Build up fencing parameters based on which Ironic driver this
|
||||||
|
# node is using
|
||||||
|
if node["pm_type"] == "pxe_ssh":
|
||||||
|
# Ironic fencing driver
|
||||||
|
node_data["agent"] = "fence_ironic"
|
||||||
|
params["action"] = self.fence_action
|
||||||
|
params["auth_url"] = self.os_auth["auth_url"]
|
||||||
|
params["login"] = self.os_auth["login"]
|
||||||
|
params["passwd"] = self.os_auth["passwd"]
|
||||||
|
params["tenant_name"] = self.os_auth["tenant_name"]
|
||||||
|
params["pcmk_host_map"] = "%(compute_name)s:%(bm_name)s" % (
|
||||||
|
{"compute_name": hostmap[mac_addr]["compute_name"],
|
||||||
|
"bm_name": hostmap[mac_addr]["baremetal_name"]})
|
||||||
|
if self.delay:
|
||||||
|
params["delay"] = self.delay
|
||||||
|
elif node["pm_type"].split('_')[1] in ("ipmitool", "ilo", "drac"):
|
||||||
|
# IPMI fencing driver
|
||||||
|
node_data["agent"] = "fence_ipmilan"
|
||||||
|
params["action"] = self.fence_action
|
||||||
|
params["ipaddr"] = node["pm_addr"]
|
||||||
|
params["passwd"] = node["pm_password"]
|
||||||
|
params["login"] = node["pm_user"]
|
||||||
|
params["pcmk_host_list"] = hostmap[mac_addr]["compute_name"]
|
||||||
|
if "pm_port" in node:
|
||||||
|
params["ipport"] = node["pm_port"]
|
||||||
|
if self.ipmi_lanplus:
|
||||||
|
params["lanplus"] = self.ipmi_lanplus
|
||||||
|
if self.delay:
|
||||||
|
params["delay"] = self.delay
|
||||||
|
if self.ipmi_cipher:
|
||||||
|
params["cipher"] = self.ipmi_cipher
|
||||||
|
if self.ipmi_level:
|
||||||
|
params["privlvl"] = self.ipmi_level
|
||||||
|
else:
|
||||||
|
error = ("Unable to generate fencing parameters for %s" %
|
||||||
|
node["pm_type"])
|
||||||
|
raise ValueError(error)
|
||||||
|
|
||||||
|
node_data["params"] = params
|
||||||
|
devices.append(node_data)
|
||||||
|
|
||||||
|
fence_params["FencingConfig"]["devices"] = devices
|
||||||
|
return {"parameter_defaults": fence_params}
|
||||||
|
@ -497,3 +497,99 @@ class GetPasswordsActionTest(base.TestCase):
|
|||||||
|
|
||||||
# ensure old passwords used and no new generation
|
# ensure old passwords used and no new generation
|
||||||
self.assertEqual(_EXISTING_PASSWORDS, result)
|
self.assertEqual(_EXISTING_PASSWORDS, result)
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateFencingParametersActionTestCase(base.TestCase):
|
||||||
|
|
||||||
|
@mock.patch('tripleo_common.utils.nodes.'
|
||||||
|
'generate_hostmap')
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||||
|
'get_compute_client')
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||||
|
'get_baremetal_client')
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||||
|
'get_workflow_client')
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||||
|
'get_orchestration_client')
|
||||||
|
@mock.patch('mistral.context.ctx')
|
||||||
|
def test_no_success(self, mock_ctx, mock_get_orchestration,
|
||||||
|
mock_get_workflow, mock_get_baremetal,
|
||||||
|
mock_get_compute, mock_generate_hostmap):
|
||||||
|
test_hostmap = {
|
||||||
|
"00:11:22:33:44:55": {
|
||||||
|
"compute_name": "compute_name_0",
|
||||||
|
"baremetal_name": "baremetal_name_0"
|
||||||
|
},
|
||||||
|
"11:22:33:44:55:66": {
|
||||||
|
"compute_name": "compute_name_1",
|
||||||
|
"baremetal_name": "baremetal_name_1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mock_generate_hostmap.return_value = test_hostmap
|
||||||
|
|
||||||
|
test_envjson = [{
|
||||||
|
"name": "control-0",
|
||||||
|
"pm_password": "control-0-password",
|
||||||
|
"pm_type": "pxe_ipmitool",
|
||||||
|
"pm_user": "control-0-admin",
|
||||||
|
"pm_addr": "0.1.2.3",
|
||||||
|
"pm_port": "0123",
|
||||||
|
"mac": [
|
||||||
|
"00:11:22:33:44:55"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"name": "control-1",
|
||||||
|
"pm_password": "control-1-password",
|
||||||
|
"pm_type": "pxe_ssh",
|
||||||
|
"pm_user": "control-1-admin",
|
||||||
|
"pm_addr": "1.2.3.4",
|
||||||
|
"mac": [
|
||||||
|
"11:22:33:44:55:66"
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
test_osauth = {
|
||||||
|
"auth_url": "test://auth.url",
|
||||||
|
"login": "test_os_username",
|
||||||
|
"passwd": "test_os_password",
|
||||||
|
"tenant_name": "test_os_tenant_name",
|
||||||
|
}
|
||||||
|
|
||||||
|
action = parameters.GenerateFencingParametersAction(test_envjson,
|
||||||
|
test_osauth,
|
||||||
|
"test_action",
|
||||||
|
28,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
True)
|
||||||
|
|
||||||
|
result = action.run()["parameter_defaults"]
|
||||||
|
|
||||||
|
self.assertTrue(result["EnableFencing"])
|
||||||
|
self.assertEqual(result["FencingConfig"]["devices"][0], {
|
||||||
|
"agent": "fence_ipmilan",
|
||||||
|
"host_mac": "00:11:22:33:44:55",
|
||||||
|
"params": {
|
||||||
|
"action": "test_action",
|
||||||
|
"delay": 28,
|
||||||
|
"ipaddr": "0.1.2.3",
|
||||||
|
"ipport": "0123",
|
||||||
|
"lanplus": True,
|
||||||
|
"privlvl": 5,
|
||||||
|
"login": "control-0-admin",
|
||||||
|
"passwd": "control-0-password",
|
||||||
|
"pcmk_host_list": "compute_name_0"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.assertEqual(result["FencingConfig"]["devices"][1], {
|
||||||
|
"agent": "fence_ironic",
|
||||||
|
"host_mac": "11:22:33:44:55:66",
|
||||||
|
"params": {
|
||||||
|
"auth_url": "test://auth.url",
|
||||||
|
"delay": 28,
|
||||||
|
"action": "test_action",
|
||||||
|
"login": "test_os_username",
|
||||||
|
"passwd": "test_os_password",
|
||||||
|
"tenant_name": "test_os_tenant_name",
|
||||||
|
"pcmk_host_map": "compute_name_1:baremetal_name_1"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@ -432,3 +432,14 @@ def update_node_capability(node_uuid, capability, value, client):
|
|||||||
node = client.node.get(node_uuid)
|
node = client.node.get(node_uuid)
|
||||||
patch = _get_capability_patch(node, capability, value)
|
patch = _get_capability_patch(node, capability, value)
|
||||||
return client.node.update(node_uuid, patch)
|
return client.node.update(node_uuid, patch)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_hostmap(baremetal_client, compute_client):
|
||||||
|
"""Create a map between Compute nodes and Baremetal nodes"""
|
||||||
|
hostmap = {}
|
||||||
|
for node in compute_client.servers.list():
|
||||||
|
bm_node = baremetal_client.node.get_by_instance_uuid(node.id)
|
||||||
|
for port in baremetal_client.port.list(node=bm_node.uuid):
|
||||||
|
hostmap[port.address] = {"compute_name": node.name,
|
||||||
|
"baremetal_name": bm_node.name}
|
||||||
|
return hostmap
|
||||||
|
Loading…
Reference in New Issue
Block a user