Completely remove support for setting IPMI credentials
This experimental feature was deprecated in the Ocata release, as it was found unstable, untested and dangerous. API version is bumped to 1.12 to indicate this change to users. Change-Id: I1aad6ddfd03946edc19ae510accd6c8daf5fc268 Closes-Bug: #1654318
This commit is contained in:
parent
7a0be9aeb0
commit
e05257035c
|
@ -15,11 +15,6 @@ done prior to calling the endpoint.
|
|||
|
||||
Requires X-Auth-Token header with Keystone token for authentication.
|
||||
|
||||
Deprecated parameters (only available in API before version ``1.9``):
|
||||
|
||||
* ``new_ipmi_password``
|
||||
* ``new_ipmi_username``
|
||||
|
||||
Response:
|
||||
|
||||
* 202 - accepted introspection request
|
||||
|
@ -289,12 +284,7 @@ Response:
|
|||
* 403 - node is not on introspection
|
||||
* 404 - node cannot be found or multiple nodes found
|
||||
|
||||
Response body: JSON dictionary. If setting IPMI credentials (deprecated
|
||||
feature) is requested, body will contain the following keys:
|
||||
|
||||
* ``ipmi_setup_credentials`` boolean ``True``
|
||||
* ``ipmi_username`` new IPMI user name
|
||||
* ``ipmi_password`` new IPMI password
|
||||
Response body: JSON dictionary with ``uuid`` key.
|
||||
|
||||
.. _hardware inventory: http://docs.openstack.org/developer/ironic-python-agent/#hardware-inventory
|
||||
.. _Specifying the disk for deployment root device hints:
|
||||
|
@ -398,3 +388,5 @@ Version History
|
|||
* **1.10** adds node state to the GET /v1/introspection/<Node ID> and
|
||||
GET /v1/introspection API response data.
|
||||
* **1.11** adds invert&multiple fields into rules response data
|
||||
* **1.12** this version indicates that support for setting IPMI credentials
|
||||
was completely removed from API (all versions).
|
||||
|
|
|
@ -320,7 +320,6 @@ These steps are avoided, based on the feature requirements:
|
|||
|
||||
Limitations:
|
||||
|
||||
* IPMI credentials are not updated --- ramdisk not running
|
||||
* there's no way to update the unprocessed data atm.
|
||||
* the unprocessed data is never cleaned from the store
|
||||
* check for stored data presence is performed in background;
|
||||
|
|
|
@ -27,7 +27,6 @@ LOG = utils.getProcessingLogger(__name__)
|
|||
|
||||
# See http://specs.openstack.org/openstack/ironic-specs/specs/kilo/new-ironic-state-machine.html # noqa
|
||||
VALID_STATES = {'enroll', 'manageable', 'inspecting', 'inspect failed'}
|
||||
SET_CREDENTIALS_VALID_STATES = {'enroll'}
|
||||
|
||||
# 1.19 is API version, which supports port.pxe_enabled
|
||||
DEFAULT_IRONIC_API_VERSION = '1.19'
|
||||
|
@ -143,15 +142,9 @@ def get_client(token=None,
|
|||
return client.Client(1, **args)
|
||||
|
||||
|
||||
def check_provision_state(node, with_credentials=False):
|
||||
def check_provision_state(node):
|
||||
state = node.provision_state.lower()
|
||||
if with_credentials and state not in SET_CREDENTIALS_VALID_STATES:
|
||||
msg = _('Invalid provision state for setting IPMI credentials: '
|
||||
'"%(state)s", valid states are %(valid)s')
|
||||
raise utils.Error(msg % {'state': state,
|
||||
'valid': list(SET_CREDENTIALS_VALID_STATES)},
|
||||
node_info=node)
|
||||
elif not with_credentials and state not in VALID_STATES:
|
||||
if state not in VALID_STATES:
|
||||
msg = _('Invalid provision state for introspection: '
|
||||
'"%(state)s", valid states are "%(valid)s"')
|
||||
raise utils.Error(msg % {'state': state, 'valid': list(VALID_STATES)},
|
||||
|
|
|
@ -76,12 +76,6 @@ PROCESSING_OPTS = [
|
|||
help=_('Whether to overwrite existing values in node '
|
||||
'database. Disable this option to make '
|
||||
'introspection a non-destructive operation.')),
|
||||
cfg.BoolOpt('enable_setting_ipmi_credentials',
|
||||
default=False,
|
||||
help=_('Whether to enable setting IPMI credentials during '
|
||||
'introspection. This feature will be removed in the '
|
||||
'Pike release.'),
|
||||
deprecated_for_removal=True),
|
||||
cfg.StrOpt('default_processing_hooks',
|
||||
default='ramdisk_error,root_disk_selection,scheduler,'
|
||||
'validate_interfaces,capabilities,pci_devices',
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"""Handling introspection request."""
|
||||
|
||||
import re
|
||||
import string
|
||||
import time
|
||||
|
||||
from eventlet import semaphore
|
||||
|
@ -31,67 +30,32 @@ CONF = cfg.CONF
|
|||
|
||||
|
||||
LOG = utils.getProcessingLogger(__name__)
|
||||
PASSWORD_ACCEPTED_CHARS = set(string.ascii_letters + string.digits)
|
||||
PASSWORD_MAX_LENGTH = 20 # IPMI v2.0
|
||||
|
||||
_LAST_INTROSPECTION_TIME = 0
|
||||
_LAST_INTROSPECTION_LOCK = semaphore.BoundedSemaphore()
|
||||
|
||||
|
||||
def _validate_ipmi_credentials(node, new_ipmi_credentials):
|
||||
if not CONF.processing.enable_setting_ipmi_credentials:
|
||||
raise utils.Error(
|
||||
_('IPMI credentials setup is disabled in configuration'))
|
||||
|
||||
new_username, new_password = new_ipmi_credentials
|
||||
if not new_username:
|
||||
new_username = node.driver_info.get('ipmi_username')
|
||||
if not new_username:
|
||||
raise utils.Error(_('Setting IPMI credentials requested, but neither '
|
||||
'new user name nor driver_info[ipmi_username] '
|
||||
'are provided'),
|
||||
node_info=node)
|
||||
wrong_chars = {c for c in new_password
|
||||
if c not in PASSWORD_ACCEPTED_CHARS}
|
||||
if wrong_chars:
|
||||
raise utils.Error(_('Forbidden characters encountered in new IPMI '
|
||||
'password: "%s"; use only letters and numbers')
|
||||
% ''.join(wrong_chars), node_info=node)
|
||||
if not 0 < len(new_password) <= PASSWORD_MAX_LENGTH:
|
||||
raise utils.Error(_('IPMI password length should be > 0 and <= %d')
|
||||
% PASSWORD_MAX_LENGTH, node_info=node)
|
||||
|
||||
return new_username, new_password
|
||||
|
||||
|
||||
def introspect(node_id, new_ipmi_credentials=None, token=None):
|
||||
def introspect(node_id, token=None):
|
||||
"""Initiate hardware properties introspection for a given node.
|
||||
|
||||
:param node_id: node UUID or name
|
||||
:param new_ipmi_credentials: tuple (new username, new password) or None
|
||||
:param token: authentication token
|
||||
:raises: Error
|
||||
"""
|
||||
ironic = ir_utils.get_client(token)
|
||||
node = ir_utils.get_node(node_id, ironic=ironic)
|
||||
|
||||
ir_utils.check_provision_state(node, with_credentials=new_ipmi_credentials)
|
||||
|
||||
if new_ipmi_credentials:
|
||||
new_ipmi_credentials = (
|
||||
_validate_ipmi_credentials(node, new_ipmi_credentials))
|
||||
else:
|
||||
validation = ironic.node.validate(node.uuid)
|
||||
if not validation.power['result']:
|
||||
msg = _('Failed validation of power interface, reason: %s')
|
||||
raise utils.Error(msg % validation.power['reason'],
|
||||
node_info=node)
|
||||
ir_utils.check_provision_state(node)
|
||||
validation = ironic.node.validate(node.uuid)
|
||||
if not validation.power['result']:
|
||||
msg = _('Failed validation of power interface, reason: %s')
|
||||
raise utils.Error(msg % validation.power['reason'],
|
||||
node_info=node)
|
||||
|
||||
bmc_address = ir_utils.get_ipmi_address(node)
|
||||
node_info = node_cache.start_introspection(node.uuid,
|
||||
bmc_address=bmc_address,
|
||||
ironic=ironic)
|
||||
node_info.set_option('new_ipmi_credentials', new_ipmi_credentials)
|
||||
|
||||
def _handle_exceptions(fut):
|
||||
try:
|
||||
|
@ -111,17 +75,16 @@ def introspect(node_id, new_ipmi_credentials=None, token=None):
|
|||
def _background_introspect(ironic, node_info):
|
||||
global _LAST_INTROSPECTION_TIME
|
||||
|
||||
if not node_info.options.get('new_ipmi_credentials'):
|
||||
if re.match(CONF.introspection_delay_drivers, node_info.node().driver):
|
||||
LOG.debug('Attempting to acquire lock on last introspection time')
|
||||
with _LAST_INTROSPECTION_LOCK:
|
||||
delay = (_LAST_INTROSPECTION_TIME - time.time()
|
||||
+ CONF.introspection_delay)
|
||||
if delay > 0:
|
||||
LOG.debug('Waiting %d seconds before sending the next '
|
||||
'node on introspection', delay)
|
||||
time.sleep(delay)
|
||||
_LAST_INTROSPECTION_TIME = time.time()
|
||||
if re.match(CONF.introspection_delay_drivers, node_info.node().driver):
|
||||
LOG.debug('Attempting to acquire lock on last introspection time')
|
||||
with _LAST_INTROSPECTION_LOCK:
|
||||
delay = (_LAST_INTROSPECTION_TIME - time.time()
|
||||
+ CONF.introspection_delay)
|
||||
if delay > 0:
|
||||
LOG.debug('Waiting %d seconds before sending the next '
|
||||
'node on introspection', delay)
|
||||
time.sleep(delay)
|
||||
_LAST_INTROSPECTION_TIME = time.time()
|
||||
|
||||
node_info.acquire_lock()
|
||||
try:
|
||||
|
@ -151,26 +114,21 @@ def _background_introspect_locked(node_info, ironic):
|
|||
LOG.info('The following attributes will be used for look up: %s',
|
||||
attrs, node_info=node_info)
|
||||
|
||||
if not node_info.options.get('new_ipmi_credentials'):
|
||||
try:
|
||||
ironic.node.set_boot_device(node_info.uuid, 'pxe',
|
||||
persistent=False)
|
||||
except Exception as exc:
|
||||
LOG.warning('Failed to set boot device to PXE: %s',
|
||||
exc, node_info=node_info)
|
||||
try:
|
||||
ironic.node.set_boot_device(node_info.uuid, 'pxe',
|
||||
persistent=False)
|
||||
except Exception as exc:
|
||||
LOG.warning('Failed to set boot device to PXE: %s',
|
||||
exc, node_info=node_info)
|
||||
|
||||
try:
|
||||
ironic.node.set_power_state(node_info.uuid, 'reboot')
|
||||
except Exception as exc:
|
||||
raise utils.Error(_('Failed to power on the node, check it\'s '
|
||||
'power management configuration: %s'),
|
||||
exc, node_info=node_info)
|
||||
LOG.info('Introspection started successfully',
|
||||
node_info=node_info)
|
||||
else:
|
||||
LOG.info('Introspection environment is ready, manual power on is '
|
||||
'required within %d seconds', CONF.timeout,
|
||||
node_info=node_info)
|
||||
try:
|
||||
ironic.node.set_power_state(node_info.uuid, 'reboot')
|
||||
except Exception as exc:
|
||||
raise utils.Error(_('Failed to power on the node, check it\'s '
|
||||
'power management configuration: %s'),
|
||||
exc, node_info=node_info)
|
||||
LOG.info('Introspection started successfully',
|
||||
node_info=node_info)
|
||||
|
||||
|
||||
def abort(node_id, token=None):
|
||||
|
|
|
@ -48,10 +48,8 @@ app = flask.Flask(__name__)
|
|||
LOG = utils.getProcessingLogger(__name__)
|
||||
|
||||
MINIMUM_API_VERSION = (1, 0)
|
||||
# TODO(dtantsur): set to the current version as soon we move setting IPMI
|
||||
# credentials support completely.
|
||||
DEFAULT_API_VERSION = (1, 8)
|
||||
CURRENT_API_VERSION = (1, 11)
|
||||
CURRENT_API_VERSION = (1, 12)
|
||||
DEFAULT_API_VERSION = CURRENT_API_VERSION
|
||||
_LOGGING_EXCLUDED_KEYS = ('logs',)
|
||||
|
||||
|
||||
|
@ -214,23 +212,7 @@ def api_introspection(node_id):
|
|||
utils.check_auth(flask.request)
|
||||
|
||||
if flask.request.method == 'POST':
|
||||
new_ipmi_password = flask.request.args.get('new_ipmi_password',
|
||||
type=str,
|
||||
default=None)
|
||||
if new_ipmi_password:
|
||||
new_ipmi_username = flask.request.args.get('new_ipmi_username',
|
||||
type=str,
|
||||
default=None)
|
||||
new_ipmi_credentials = (new_ipmi_username, new_ipmi_password)
|
||||
else:
|
||||
new_ipmi_credentials = None
|
||||
|
||||
if new_ipmi_credentials and _get_version() >= (1, 9):
|
||||
return _('Setting IPMI credentials is deprecated and not allowed '
|
||||
'starting with API version 1.9'), 400
|
||||
|
||||
introspect.introspect(node_id,
|
||||
new_ipmi_credentials=new_ipmi_credentials,
|
||||
token=flask.request.headers.get('X-Auth-Token'))
|
||||
return '', 202
|
||||
else:
|
||||
|
|
|
@ -18,7 +18,6 @@ import datetime
|
|||
import json
|
||||
import os
|
||||
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import base64
|
||||
from oslo_utils import excutils
|
||||
|
@ -38,8 +37,6 @@ CONF = cfg.CONF
|
|||
|
||||
LOG = utils.getProcessingLogger(__name__)
|
||||
|
||||
_CREDENTIALS_WAIT_RETRIES = 10
|
||||
_CREDENTIALS_WAIT_PERIOD = 3
|
||||
_STORAGE_EXCLUDED_KEYS = {'logs'}
|
||||
_UNPROCESSED_DATA_STORE_SUFFIX = 'UNPROCESSED'
|
||||
|
||||
|
@ -279,56 +276,12 @@ def _process_node(node_info, node, introspection_data):
|
|||
|
||||
resp = {'uuid': node.uuid}
|
||||
|
||||
if node_info.options.get('new_ipmi_credentials'):
|
||||
new_username, new_password = (
|
||||
node_info.options.get('new_ipmi_credentials'))
|
||||
utils.executor().submit(_finish_set_ipmi_credentials,
|
||||
node_info, ironic, node, introspection_data,
|
||||
new_username, new_password)
|
||||
resp['ipmi_setup_credentials'] = True
|
||||
resp['ipmi_username'] = new_username
|
||||
resp['ipmi_password'] = new_password
|
||||
else:
|
||||
utils.executor().submit(_finish, node_info, ironic, introspection_data,
|
||||
power_off=CONF.processing.power_off)
|
||||
utils.executor().submit(_finish, node_info, ironic, introspection_data,
|
||||
power_off=CONF.processing.power_off)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@node_cache.fsm_transition(istate.Events.finish)
|
||||
def _finish_set_ipmi_credentials(node_info, ironic, node, introspection_data,
|
||||
new_username, new_password):
|
||||
patch = [{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
'value': new_username},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': new_password}]
|
||||
new_ipmi_address = utils.get_ipmi_address_from_data(introspection_data)
|
||||
if not ir_utils.get_ipmi_address(node) and new_ipmi_address:
|
||||
patch.append({'op': 'add', 'path': '/driver_info/ipmi_address',
|
||||
'value': new_ipmi_address})
|
||||
node_info.patch(patch)
|
||||
|
||||
for attempt in range(_CREDENTIALS_WAIT_RETRIES):
|
||||
try:
|
||||
# We use this call because it requires valid credentials.
|
||||
# We don't care about boot device, obviously.
|
||||
ironic.node.get_boot_device(node_info.uuid)
|
||||
except Exception as exc:
|
||||
LOG.info('Waiting for credentials update, attempt %(attempt)d '
|
||||
'current error is %(exc)s',
|
||||
{'attempt': attempt, 'exc': exc},
|
||||
node_info=node_info, data=introspection_data)
|
||||
eventlet.greenthread.sleep(_CREDENTIALS_WAIT_PERIOD)
|
||||
else:
|
||||
_finish_common(node_info, ironic, introspection_data)
|
||||
return
|
||||
|
||||
msg = (_('Failed to validate updated IPMI credentials for node '
|
||||
'%s, node might require maintenance') % node_info.uuid)
|
||||
node_info.finished(error=msg)
|
||||
raise utils.Error(msg, node_info=node_info, data=introspection_data)
|
||||
|
||||
|
||||
def _finish_common(node_info, ironic, introspection_data, power_off=True):
|
||||
if power_off:
|
||||
LOG.debug('Forcing power off of node %s', node_info.uuid)
|
||||
|
|
|
@ -54,8 +54,6 @@ os_password = password
|
|||
os_tenant_name = tenant
|
||||
[firewall]
|
||||
manage_firewall = False
|
||||
[processing]
|
||||
enable_setting_ipmi_credentials = True
|
||||
[DEFAULT]
|
||||
debug = True
|
||||
auth_strategy = noauth
|
||||
|
@ -163,13 +161,8 @@ class Base(base.NodeTest):
|
|||
raise AssertionError(msg)
|
||||
return res
|
||||
|
||||
def call_introspect(self, uuid, new_ipmi_username=None,
|
||||
new_ipmi_password=None, **kwargs):
|
||||
def call_introspect(self, uuid, **kwargs):
|
||||
endpoint = '/v1/introspection/%s' % uuid
|
||||
if new_ipmi_password:
|
||||
endpoint += '?new_ipmi_password=%s' % new_ipmi_password
|
||||
if new_ipmi_username:
|
||||
endpoint += '&new_ipmi_username=%s' % new_ipmi_username
|
||||
return self.call('post', endpoint, **kwargs)
|
||||
|
||||
def call_get_status(self, uuid, **kwargs):
|
||||
|
@ -309,37 +302,6 @@ class Test(Base):
|
|||
status = self.call_get_status(self.uuid)
|
||||
self.check_status(status, finished=True, state=istate.States.finished)
|
||||
|
||||
def test_setup_ipmi(self):
|
||||
patch_credentials = [
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
'value': 'admin'},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': 'pwd'},
|
||||
]
|
||||
self.node.provision_state = 'enroll'
|
||||
self.call_introspect(self.uuid, new_ipmi_username='admin',
|
||||
new_ipmi_password='pwd')
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
self.assertFalse(self.cli.node.set_power_state.called)
|
||||
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.check_status(status, finished=False, state=istate.States.waiting)
|
||||
|
||||
res = self.call_continue(self.data)
|
||||
self.assertEqual('admin', res['ipmi_username'])
|
||||
self.assertEqual('pwd', res['ipmi_password'])
|
||||
self.assertTrue(res['ipmi_setup_credentials'])
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
|
||||
self.assertCalledWithPatch(self.patch + patch_credentials,
|
||||
self.cli.node.update)
|
||||
self.cli.port.create.assert_called_once_with(
|
||||
node_uuid=self.uuid, address='11:22:33:44:55:66', extra={},
|
||||
pxe_enabled=True)
|
||||
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.check_status(status, finished=True, state=istate.States.finished)
|
||||
|
||||
def test_introspection_statuses(self):
|
||||
self.call_introspect(self.uuid)
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
|
|
|
@ -72,8 +72,6 @@ class TestIntrospect(BaseTest):
|
|||
persistent=False)
|
||||
cli.node.set_power_state.assert_called_once_with(self.uuid,
|
||||
'reboot')
|
||||
self.node_info.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', None)
|
||||
self.node_info.acquire_lock.assert_called_once_with()
|
||||
self.node_info.release_lock.assert_called_once_with()
|
||||
|
||||
|
@ -99,8 +97,6 @@ class TestIntrospect(BaseTest):
|
|||
persistent=False)
|
||||
cli.node.set_power_state.assert_called_once_with(self.uuid,
|
||||
'reboot')
|
||||
self.node_info.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', None)
|
||||
self.node_info.acquire_lock.assert_called_once_with()
|
||||
self.node_info.release_lock.assert_called_once_with()
|
||||
|
||||
|
@ -332,89 +328,6 @@ class TestIntrospect(BaseTest):
|
|||
self.assertEqual(42, introspect._LAST_INTROSPECTION_TIME)
|
||||
|
||||
|
||||
@mock.patch.object(firewall, 'update_filters', autospec=True)
|
||||
@mock.patch.object(node_cache, 'start_introspection', autospec=True)
|
||||
@mock.patch.object(ir_utils, 'get_client', autospec=True)
|
||||
class TestSetIpmiCredentials(BaseTest):
|
||||
def setUp(self):
|
||||
super(TestSetIpmiCredentials, self).setUp()
|
||||
CONF.set_override('enable_setting_ipmi_credentials', True,
|
||||
'processing')
|
||||
self.new_creds = ('user', 'password')
|
||||
self.node_info.options['new_ipmi_credentials'] = self.new_creds
|
||||
self.node.provision_state = 'enroll'
|
||||
|
||||
def test_ok(self, client_mock, start_mock, filters_mock):
|
||||
cli = self._prepare(client_mock)
|
||||
start_mock.return_value = self.node_info
|
||||
|
||||
introspect.introspect(self.uuid, new_ipmi_credentials=self.new_creds)
|
||||
|
||||
start_mock.assert_called_once_with(self.uuid,
|
||||
bmc_address=self.bmc_address,
|
||||
ironic=cli)
|
||||
filters_mock.assert_called_with(cli)
|
||||
self.assertFalse(cli.node.validate.called)
|
||||
self.assertFalse(cli.node.set_boot_device.called)
|
||||
self.assertFalse(cli.node.set_power_state.called)
|
||||
start_mock.return_value.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', self.new_creds)
|
||||
|
||||
def test_disabled(self, client_mock, start_mock, filters_mock):
|
||||
CONF.set_override('enable_setting_ipmi_credentials', False,
|
||||
'processing')
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaisesRegex(utils.Error, 'disabled',
|
||||
introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
def test_no_username(self, client_mock, start_mock, filters_mock):
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=(None, 'password'))
|
||||
|
||||
def test_default_username(self, client_mock, start_mock, filters_mock):
|
||||
cli = self._prepare(client_mock)
|
||||
start_mock.return_value = self.node_info
|
||||
self.node.driver_info['ipmi_username'] = self.new_creds[0]
|
||||
|
||||
introspect.introspect(self.uuid,
|
||||
new_ipmi_credentials=(None, self.new_creds[1]))
|
||||
|
||||
start_mock.assert_called_once_with(self.uuid,
|
||||
bmc_address=self.bmc_address,
|
||||
ironic=cli)
|
||||
filters_mock.assert_called_with(cli)
|
||||
self.assertFalse(cli.node.validate.called)
|
||||
self.assertFalse(cli.node.set_boot_device.called)
|
||||
self.assertFalse(cli.node.set_power_state.called)
|
||||
start_mock.return_value.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', self.new_creds)
|
||||
|
||||
def test_wrong_letters(self, client_mock, start_mock, filters_mock):
|
||||
self.new_creds = ('user', 'p ssw@rd')
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
def test_too_long(self, client_mock, start_mock, filters_mock):
|
||||
self.new_creds = ('user', 'password' * 100)
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
def test_wrong_state(self, client_mock, start_mock, filters_mock):
|
||||
self.node.provision_state = 'manageable'
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
|
||||
@mock.patch.object(firewall, 'update_filters', autospec=True)
|
||||
@mock.patch.object(node_cache, 'get_node', autospec=True)
|
||||
@mock.patch.object(ir_utils, 'get_client', autospec=True)
|
||||
|
|
|
@ -59,38 +59,8 @@ class TestApiIntrospect(BaseAPITest):
|
|||
res = self.app.post('/v1/introspection/%s' % self.uuid)
|
||||
self.assertEqual(202, res.status_code)
|
||||
introspect_mock.assert_called_once_with(self.uuid,
|
||||
new_ipmi_credentials=None,
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_introspect_set_ipmi_credentials(self, introspect_mock):
|
||||
res = self.app.post('/v1/introspection/%s?new_ipmi_username=user&'
|
||||
'new_ipmi_password=password' % self.uuid)
|
||||
self.assertEqual(202, res.status_code)
|
||||
introspect_mock.assert_called_once_with(
|
||||
self.uuid,
|
||||
new_ipmi_credentials=('user', 'password'),
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_introspect_set_ipmi_credentials_disabled(self, introspect_mock):
|
||||
headers = {conf.VERSION_HEADER: '1.9'}
|
||||
res = self.app.post('/v1/introspection/%s?new_ipmi_username=user&'
|
||||
'new_ipmi_password=password' % self.uuid,
|
||||
headers=headers)
|
||||
self.assertEqual(400, res.status_code)
|
||||
self.assertFalse(introspect_mock.called)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_introspect_set_ipmi_credentials_no_user(self, introspect_mock):
|
||||
res = self.app.post('/v1/introspection/%s?'
|
||||
'new_ipmi_password=password' % self.uuid)
|
||||
self.assertEqual(202, res.status_code)
|
||||
introspect_mock.assert_called_once_with(
|
||||
self.uuid,
|
||||
new_ipmi_credentials=(None, 'password'),
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_intospect_failed(self, introspect_mock):
|
||||
introspect_mock.side_effect = utils.Error("boom")
|
||||
|
@ -101,7 +71,6 @@ class TestApiIntrospect(BaseAPITest):
|
|||
json.loads(res.data.decode('utf-8'))['error']['message'])
|
||||
introspect_mock.assert_called_once_with(
|
||||
self.uuid,
|
||||
new_ipmi_credentials=None,
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(utils, 'check_auth', autospec=True)
|
||||
|
|
|
@ -353,14 +353,6 @@ class TestProcessNode(BaseTest):
|
|||
self.data['interfaces'] = self.valid_interfaces
|
||||
self.ports = self.all_ports
|
||||
|
||||
self.new_creds = ('user', 'password')
|
||||
self.patch_credentials = [
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
'value': self.new_creds[0]},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': self.new_creds[1]},
|
||||
]
|
||||
|
||||
self.cli.node.get_boot_device.side_effect = (
|
||||
[RuntimeError()] * self.validate_attempts + [None])
|
||||
self.cli.port.create.side_effect = self.ports
|
||||
|
@ -382,12 +374,6 @@ class TestProcessNode(BaseTest):
|
|||
ret_val = process._process_node(self.node_info, self.node, self.data)
|
||||
self.assertEqual(self.uuid, ret_val.get('uuid'))
|
||||
|
||||
def test_return_includes_uuid_with_ipmi_creds(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
ret_val = process._process_node(self.node_info, self.node, self.data)
|
||||
self.assertEqual(self.uuid, ret_val.get('uuid'))
|
||||
self.assertTrue(ret_val.get('ipmi_setup_credentials'))
|
||||
|
||||
@mock.patch.object(example_plugin.ExampleProcessingHook, 'before_update')
|
||||
def test_wrong_provision_state(self, post_hook_mock):
|
||||
self.node.provision_state = 'active'
|
||||
|
@ -428,49 +414,6 @@ class TestProcessNode(BaseTest):
|
|||
address=self.macs[1],
|
||||
extra={}, pxe_enabled=False)
|
||||
|
||||
def test_set_ipmi_credentials(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
|
||||
process._process_node(self.node_info, self.node, self.data)
|
||||
|
||||
self.cli.node.update.assert_any_call(self.uuid, self.patch_credentials)
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid, 'off')
|
||||
self.cli.node.get_boot_device.assert_called_with(self.uuid)
|
||||
self.assertEqual(self.validate_attempts + 1,
|
||||
self.cli.node.get_boot_device.call_count)
|
||||
|
||||
def test_set_ipmi_credentials_no_address(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
del self.node.driver_info['ipmi_address']
|
||||
self.patch_credentials.append({'op': 'add',
|
||||
'path': '/driver_info/ipmi_address',
|
||||
'value': self.bmc_address})
|
||||
|
||||
process._process_node(self.node_info, self.node, self.data)
|
||||
|
||||
self.cli.node.update.assert_any_call(self.uuid, self.patch_credentials)
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid, 'off')
|
||||
self.cli.node.get_boot_device.assert_called_with(self.uuid)
|
||||
self.assertEqual(self.validate_attempts + 1,
|
||||
self.cli.node.get_boot_device.call_count)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'finished', autospec=True)
|
||||
def test_set_ipmi_credentials_timeout(self, finished_mock):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
self.cli.node.get_boot_device.side_effect = RuntimeError('boom')
|
||||
|
||||
process._process_node(self.node_info, self.node, self.data)
|
||||
|
||||
self.cli.node.update.assert_any_call(self.uuid, self.patch_credentials)
|
||||
self.assertEqual(2, self.cli.node.update.call_count)
|
||||
self.assertEqual(process._CREDENTIALS_WAIT_RETRIES,
|
||||
self.cli.node.get_boot_device.call_count)
|
||||
self.assertFalse(self.cli.node.set_power_state.called)
|
||||
finished_mock.assert_called_once_with(
|
||||
mock.ANY,
|
||||
error='Failed to validate updated IPMI credentials for node %s, '
|
||||
'node might require maintenance' % self.uuid)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'finished', autospec=True)
|
||||
def test_power_off_failed(self, finished_mock):
|
||||
self.cli.node.set_power_state.side_effect = RuntimeError('boom')
|
||||
|
@ -609,7 +552,6 @@ class TestReapplyNode(BaseTest):
|
|||
started_at=self.started_at,
|
||||
node=self.node)
|
||||
self.node_info.invalidate_cache = mock.Mock()
|
||||
self.new_creds = ('user', 'password')
|
||||
|
||||
self.cli.port.create.side_effect = self.ports
|
||||
self.cli.node.update.return_value = self.node
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
Experimental setting IPMI credentials support was removed from all versions
|
||||
of the API. The current API version was bumped to 1.12 to mark this change.
|
||||
- |
|
||||
The default API version was synchronized with the current API version again
|
||||
after removal of the IPMI credentials setting.
|
Loading…
Reference in New Issue