Add overcloud_parameters.GenerateFencingParameters.

We can now generate parameters to enable HA fencing, by re-consuming the
instackenv.json file and introspecting the deployed stack.

Closes-Bug: #1649695

Depends-On: I0ff173b6daf770329b25f61b76fcba0e28b2550a
Change-Id: I8b717b069f95cac20fe2d8c7fdaaacc46214957b
This commit is contained in:
Chris Jones 2016-12-15 21:07:16 +00:00
parent 9c4b435b04
commit eb4686cfc1
4 changed files with 172 additions and 0 deletions

View File

@ -0,0 +1,6 @@
---
features:
- |
Adds `overcloud generate fencing` command, which outputs an environment
file which can be used to configure node fencing in HA deployments.
Currently IPMI and virtual (non-production) deployments are supported.

View File

@ -80,5 +80,6 @@ openstack.tripleoclient.v1 =
overcloud_profiles_list = tripleoclient.v1.overcloud_profiles:ListProfiles
overcloud_update_stack = tripleoclient.v1.overcloud_update:UpdateOvercloud
overcloud_execute = tripleoclient.v1.overcloud_execute:RemoteExecute
overcloud_generate_fencing = tripleoclient.v1.overcloud_parameters:GenerateFencingParameters
undercloud_install = tripleoclient.v1.undercloud:InstallUndercloud
undercloud_upgrade = tripleoclient.v1.undercloud:UpgradeUndercloud

View File

@ -83,3 +83,91 @@ class TestSetParameters(utils.TestCommand):
self.assertRaises(
exceptions.InvalidConfiguration,
self._test_set_parameters, ".invalid", yaml.dump, {})
class TestGenerateFencingParameters(utils.TestCommand):
def setUp(self):
super(TestGenerateFencingParameters, self).setUp()
self.cmd = overcloud_parameters.GenerateFencingParameters(self.app,
None)
self.app.client_manager.workflow_engine = mock.Mock()
self.workflow = self.app.client_manager.workflow_engine
def test_generate_parameters(self):
nodes_file = tempfile.NamedTemporaryFile(suffix='.json', delete=False,
mode="wt")
self.addCleanup(os.unlink, nodes_file.name)
nodes_file.write("""
{
"nodes": [
{
"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"
]
}
]
}
""")
nodes_file.close()
os.environ["OS_USERNAME"] = "test_os_username"
os.environ["OS_PASSWORD"] = "test_os_password"
os.environ["OS_AUTH_URL"] = "test://auth.url"
os.environ["OS_TENANT_NAME"] = "test_os_tenant_name"
arglist = [nodes_file.name]
verifylist = []
self.workflow.action_executions.create.return_value = mock.MagicMock(
output='{"result":[]}')
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.workflow.action_executions.create.assert_called_once_with(
'tripleo.parameters.generate_fencing',
{
'fence_action': 'reboot',
'nodes_json': [
{
u'mac': [u'00:11:22:33:44:55'],
u'name': u'control-0',
u'pm_port': u'0123',
u'pm_addr': u'0.1.2.3',
u'pm_type': u'pxe_ipmitool',
u'pm_password': u'control-0-password',
u'pm_user': u'control-0-admin'
},
{
u'name': u'control-1',
u'pm_addr': u'1.2.3.4',
u'pm_type': u'pxe_ssh',
u'pm_user': u'control-1-admin',
u'pm_password': u'control-1-password',
u'mac': [u'11:22:33:44:55:66']
}],
'delay': None,
'os_auth': {},
'ipmi_cipher': None,
'ipmi_lanplus': False,
'ipmi_privlvl': None
},
run_sync=True, save_result=True)

View File

@ -13,12 +13,15 @@
import argparse
import json
import logging
import os
import yaml
from osc_lib.command import command
from osc_lib.i18n import _
from tripleoclient import exceptions
from tripleoclient import utils
from tripleoclient.workflows import base
from tripleoclient.workflows import parameters
@ -62,3 +65,77 @@ class SetParameters(command.Command):
container=name,
parameters=params
)
class GenerateFencingParameters(command.Command):
"""Generate fencing parameters"""
log = logging.getLogger(__name__ + ".GenerateFencing")
def get_parser(self, prog_name):
parser = super(GenerateFencingParameters, self).get_parser(prog_name)
parser.add_argument('-a', '--action', dest='fence_action',
default='reboot',
help=_('Operation to perform. Valid operations: '
'on, off, reboot, status, list, diag, '
'monitor or metadata. Defaults to: reboot'))
parser.add_argument('--delay', type=int,
help=_('Wait DELAY seconds before fencing is '
'started'))
parser.add_argument('--ipmi-lanplus',
action='store_true',
help=_('Use Lanplus. Defaults to: false'))
parser.add_argument('--ipmi-cipher', type=int,
help=_('Ciphersuite to use (same as ipmitool -C '
'parameter.'))
parser.add_argument('--ipmi-level',
help=_('Privilegel level on IPMI device. Valid '
'levels: callback, user, operator, '
'administrator.'))
parser.add_argument('--output', type=argparse.FileType('w'),
help=_('Write parameters to a file'))
parser.add_argument('instackenv', type=argparse.FileType('r'))
return parser
def take_action(self, parsed_args):
nodes_config = utils.parse_env_file(parsed_args.instackenv)
# We only need to collect OpenStack client authentication data
# if we have nodes using Ironic's pxe_ssh driver. If there are
# any, we'll use the fence_ironic fencing agent, which requires
# the auth data.
os_auth = None
for node in nodes_config:
if node["pm_type"] == "pxe_ssh":
os_auth = {}
if os_auth:
try:
os_auth["auth_url"] = os.environ["OS_AUTH_URL"]
os_auth["login"] = os.environ["OS_USERNAME"]
os_auth["passwd"] = os.environ["OS_PASSWORD"]
os_auth["tenant_name"] = os.environ["OS_TENANT_NAME"]
except KeyError:
# This really shouldn't happen since we're running in
# tripleoclient
raise ValueError(
_("Unable to find one or more of OS_AUTH_URL, "
"OS_USERNAME, OS_PASSWORD or OS_TENANT_NAME in the "
"environment."))
workflow_input = {
'nodes_json': nodes_config,
'os_auth': os_auth,
'fence_action': parsed_args.fence_action,
'delay': parsed_args.delay,
'ipmi_privlvl': parsed_args.ipmi_level,
'ipmi_cipher': parsed_args.ipmi_cipher,
'ipmi_lanplus': parsed_args.ipmi_lanplus,
}
result = base.call_action(
self.app.client_manager.workflow_engine,
'tripleo.parameters.generate_fencing',
**workflow_input)
fencing_parameters = yaml.safe_dump(result, default_flow_style=False)
if parsed_args.output:
parsed_args.output.write(fencing_parameters)
else:
print(fencing_parameters)